算法问题:若干数中取出n个数,之和最接近某个数

ashchen 2007-02-01 11:15:41
例子:有若干文件,要刻录到700M光盘里,如何选出总容量最接近700M的文件列表,不能超过700
除了遍历,求算法,php
...全文
3636 68 打赏 收藏 转发到动态 举报
写回复
用AI写文章
68 条回复
切换为时间正序
请发表友善的回复…
发表回复
ImN1 2007-06-20
  • 打赏
  • 举报
回复
这上面有几个是php的?
dh20156 2007-06-20
  • 打赏
  • 举报
回复
学习
foolbirdflyfirst 2007-06-20
  • 打赏
  • 举报
回复
mark
ashchen 2007-06-20
  • 打赏
  • 举报
回复
顶顶 大笨狼,把遍历算法优化一下算法也非常快
超级大笨狼 2007-02-09
  • 打赏
  • 举报
回复
总计:18个文件,最优方案共699兆,用时:00:02:45.9406860
选择:
1-[19],2-[36],3-[49],4-[34],5-[36],6-[46],7-[85],8-[83],9-[59],10-[50],11-[44],14-[85],15-[73]
超级大笨狼 2007-02-09
  • 打赏
  • 举报
回复
总计:699兆,用时:00:02:50.2532688
选择:
3-[42],4-[31],5-[38],6-[70],7-[59],9-[85],10-[81],11-[76],12-[91],13-[39],14-[47],16-[40]
超级大笨狼 2007-02-09
  • 打赏
  • 举报
回复
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
int totalMass = Convert.ToInt32(this.textBox2.Text);
int totalFiles = Convert.ToInt32(this.textBox1.Text);
int[] Arr = new int[totalFiles];
Random rnd = new Random();
//随便产生些大小不一的文件
for (int i = 0; i < totalFiles; i++)
{
Arr[i] = rnd.Next(100);
}
this.listBox1.DataSource = Arr;
int finalMass = 0;
int mass = 0;

int a = 0;
int b = 0;
string finalResult = "";
string result = "";
richTextBox1.Text = "正在计算,请梢侯...";
pictureBox1.Show();
DateTime T = DateTime.Now;
//开始结算
for (int x = 1; x < Math.Pow(2, totalFiles); x++)
{
result = "";
mass = 0;
b = 0;
a = x;
while (a != 0)
{
if (a % 2 != 0)
{
result += (b+1) + "-[" + Arr[b] + "],";
mass += Arr[b];
}
b++;
a >>= 1;
Application.DoEvents();
if (mass > finalMass && mass < 700)
{
finalMass = mass;
finalResult = result;
}
}
Application.DoEvents();
}
richTextBox1.Text = "总计:" + finalMass + "兆,用时:" + (DateTime.Now-T).ToString();
richTextBox1.Text +="\n选择:\n" + finalResult.Substring(0, finalResult.Length - 1);
pictureBox1.Hide();
}

private void Form1_Load(object sender, EventArgs e)
{

}





}
}
FBugFramework 2007-02-08
  • 打赏
  • 举报
回复
/**
LogList: (Example)
9;
2,3,4;
1,1,2,5;
Size++
BoxList-Style:---------------->
Box1 | f11 f21 f31 f41
Box2 | f21
..
Box5 | f51 f52
..
*/

php里应该有eval(javascript),execute(asp)等方法; 嵌套for循环层数不定。
晚上再想想。
发上来大家也可以说说方法。
FBugFramework 2007-02-08
  • 打赏
  • 举报
回复
获取数据
{
// 这个优化策略对性能很重要:
定义合理的光盘有效使用空间: diskm = 700 * 93%~97% = 650~670
diskm = 700;
diskma = 670;
diskmi = 650;
diskmm = (diskma + diskmi)/2 = 660;

定义并获取每个文件列表 : list[n][5];
list[fileIndex][0] : 文件名
list[fileIndex][1] : 文件大小
list[fileIndex][2] : 占总容量的百分率
list[fileIndex][3] : 描述list[fileIndex][2]的小数级别{ 1(0.1), 2(0.01), 3(0.001) }
list[fileIndex][4] : (int)(list[fileIndex][1] / diskm * 10)
定义并获取所有文件大小 : sum = list[1][1] + list[2][1] + ... + list[n][1];

过滤 list[fileIndex][1] > diskma 的文件
list[fileIndex][2] = list[fileIndex][1] / diskmm * 100;

选择(list[fileIndex][2])
{
如果 >= 10 :
list[fileIndex][3] = 1;
如果 >= 1 :
list[fileIndex][3] = 2;
否则 :
list[fileIndex][3] = 3;
}

}



分析数据(文件大小/和sum的比)
{
生成从大到小的 order[n][2]
order[n][0] = fileIndex
order[n][1] = ..


选择(sum / diskma)
{
如果 >1.5(算大于) :
Call方法 选取(list);
如果 >1(差不多) :
Call方法 丢弃(list);
如果 <=1 :
Call方法 全选(list);
}
}

方法 全选(list)
{
返回 全部文件;
}

方法 丢弃(list)
{
//和方法'选取'差不多只是刚好相反;
}

//重点介绍'选取'
方法 选取(list)
{
定义并获取sort[3][4];
sort[n] = list[fileIndex][3].type = {1,2,3};
sort[n][0] = 分类文件个数;
sort[n][1] = 分类文件个数占总个数的百分比;
sort[n][2] = 分类文件容量和;
sort[n][3] = 分类文件容量和占总容量的百分比;


// 生成sort信息列表
validFileCount = order.RowCount
for (int i=0; i<validFileCount; i++)
{
fileIndex = order[i][0];
sortType = list[fileIndex][3];

sort[sortType][0] ++;
sort[sortType][2] += list[fileIndex][1];
}
for (int i=0; i<sort.RowCount; i++)
{
sort[i][1] = sort[i][0] / order.Count;
sort[i][3] = sort[i][2] / sum
}

if (sort[2][2] + sort[3][2] > diskm * (97%-93%) )
{
// 有一定份量;增加可调范围并暂时忽略 文件级别!=1 的文件
// 区分是为了保证下图中的"a个"的有效性.
diskmi = diskmi - sort[2][2] - sort[3][2];
}



/**
arrSort:
0.1__1___2___3___4__5__6__7__8__9__Max
A个 B C D E F G H I J
Box1 Box3
f11 f31
f12 f3n
f13
f1n

PS:
f11,f12,f13..f1n 属Box1组, order by ASC, 共A个文件, 大小范围是: (0.1~1) * 70;
f31,f3n 属Box3组, order by ASC, 共C个文件, 大小范围是: (2~3) * 70;
------------------
看到这个图, 也就是意味着这个问题已经转换成: 在现有的a个,b个,c个数内,取整后怎么样加才有可能把这些数加得的结果是9(10是超出)!
出来一组可行选择方案; 如: 9; 2,3,4; 1,1,1,6; (1-9可理解为9个容器,里面有若干数据)
*/


arrSort[10]={0,0,0,0,0,0,0,0,0,0};
for (int i=0; i<validFileCount; i++)
{
arrSort[list[i][4]] ++;
}
// A = arrSort[0];
// B = arrSort[1];
// ...
// J = arrSort[9];


// 获取选择方式LogList;

long maxSize = 0;
long selSize = 0;
string selLog = null;

// 方法 min/max(a,b) 是获取a和b中的最小/大值;
// 方法 overflow(a) 是检测a是否超过;
// 方法 betweenand(x,a,b) 是检测x是否在a,b之间;
// 9个for嵌套循环;
// 大写的表示 个数;
for (int j=0; j<=min(J,1); j++) //最多1个
{
selSize = min(j,1)*9;

if (overflow(selSize))
break;

for (int i=0; i<=min(I,1); i++) //最多1个
{
selSize = selSize + min(i,1)*8;

if (overflow(selSize + min(i,1)*8))
break;

for (int h=0; h<min(H,1); h++) //最多1个
{
....

if (overflow(selSize + min(b,1)*2))
break;

for (int a=0; a<min(A,9); a++) //最多9个
{
if (overflow(selSize + min(a,1)*1))
break;

selSize = selSize + min(a,1)*1;

if (betweenand(selSize,9,9))
{
writeLog; // ok;
}
else
{
if (selSize > maxSize)
{
selLog = thisLog; //new maxSize
maxSize = selSize;
}
}
if (9 < selSize)
{
impossible;
}
}

selSize = selSize + min(b,1)*2;

if (betweenand(selSize,9,9))
{
writeLog; // ok;
}
else
{
if (selSize > maxSize)
{
selLog = thisLog; //new maxSize
maxSize = selSize;
}
}

....
}

if (betweenand(selSize,9,9))
{
writeLog; // ok;
}
else
{
if (selSize > maxSize)
{
selLog = thisLog; //new maxSize
maxSize = selSize;
}
}
}

if (betweenand(selSize,9,9))
{
writeLog; // ok;
}
else
{
if (selSize > maxSize)
{
selLog = thisLog; //new maxSize
maxSize = selSize;
}
}

}

/**
LogList: (Example)
9;
2,3,4;
1,1,2,5;
Size++
BoxList-Style:---------------->
Box1 | f11 f21 f31 f41
Box2 | f21
..
Box5 | f51 f52
..
*/

if (LogList is empty)
{
// 无最佳方案;用次等替换.
LogList[0] = selLog;
}

foreach log in LogList
{
// log = "1,1,2,5";
arrLog = log.split(",");


for (int i=0; i<arrLog.Length; i++)
{
for (int i=0; i<Box1.Length; i++)
{
}

.... faint.
}



}

}
FBugFramework 2007-02-08
  • 打赏
  • 举报
回复
是否要2的n次方穷举,那可真的要按照楼主的思路了,
"如何选出总容量最接近700M的文件列表" 为了最优空间,必须穷举!关于这点请先确认;

要优化,就会有一些光盘空间的浪费!只是比较少。


我认为讨论的应该就是如何做优化:穷举非为上策。适当浪费也光荣!



写写简单的伪代码吧。同时也重复一下我上面描述的思路:





先附表:
==============================================================================

/**
list表:
===========================================
fileIndex |文件名[0] 文件大小[1] 占总容量的百分率[2] 文件大小级别[3] 文件大小定位[4]
1 | aa.rar 100M ?% 1 1
2 | bb.rar 500M ?% 1 7
3 | cc.rar 50M ?% 2 0
4 | dd.rar 1K ?% 3 0
5 | ee.rar 1G ?% X(丢弃本数据) X

order表(filesize DESC):
===========================================
orderIndex |fileIndex[0] 预留
1 | 2 NULL
2 | 1 NULL
3 | 3 NULL
4 | 4 NULL

sort表:
===========================================
文件级别 |个数[0] 个数百分比[1] 容量和[2] 容量和百分比[3]
1 | 2 50% 600M 90%
2 | 1 25% 50M 9%
3 | 1 25% 1K 1%
*/

超级大笨狼 2007-02-07
  • 打赏
  • 举报
回复
2的n次方个枚举,如果你的文件超过32,就是Int32整数很难表达了,刚才用C#写了一个32个文件内的枚举。在数字是20的时候还是可以忍受。
再往上增加就慢的要命.

private void button1_Click(object sender, System.EventArgs e)
{
//total默认是8
int total=Convert.ToInt32(this.textBox1.Text);
this.progressBar1.Value=0;
int[] arr=new int[total];
System.Random r=new Random();
for(int i=0;i<total;i++)
{
arr[i]=r.Next(300);
}
this.listBox1.DataSource=arr;
this.progressBar1.Maximum=Convert.ToInt32(Math.Pow(2,total));
this.progressBar1.Minimum=0;
string aim="";
int max=700;
int maxCD=0;
System.DateTime t1=System.DateTime.Now;
for(int m=0;m<Math.Pow(2,total);m++)
{
string str=Convert.ToString(m,2);
int mass=0;
for(int j=0;j<str.Length;j++)
{
mass+=Convert.ToInt32(str.Substring(j,1)) * arr[j];
}
if(700-mass<max && mass<=700)
{
aim=str;
max=700-mass;
maxCD=mass;
}
//每大概1秒显示进度,不重要
if(System.DateTime.Now>=t1.AddSeconds(1))
{
this.progressBar1.Value=m;
this.progressBar1.Refresh();
t1=System.DateTime.Now;
}

Application.DoEvents();
}
this.progressBar1.Value=this.progressBar1.Maximum;
MessageBox.Show("最佳选择是:" + aim + "总计兆数:" + maxCD );

}
wenbin0727 2007-02-06
  • 打赏
  • 举报
回复
Mark!
FBugFramework 2007-02-05
  • 打赏
  • 举报
回复
数据的规律性不得知道,考虑算法意义就不大了。
任何算法在特殊的数据下面都可能会得出不正确的结果。
亮灯了 2007-02-05
  • 打赏
  • 举报
回复
收藏!
FBugFramework 2007-02-05
  • 打赏
  • 举报
回复
今天特意去了解了
背包问题 问题描述;
有不同价值,不同重量的物品n件,求从这n件物品中选取一部分物品的选择方案,是选中的物品总重量不超过指定的限制重量,但是选中的物品的价值之和最大.



这两个不是同一个事情。只是有些类似罢了。

背包问题里面要考虑的一个问题是: 单位空间的价值。


而楼主的问题没这个概念。只知道是有数据,并不确定是理想化的100,200,300M,可能也含有一堆1k 2k的文件!要做优化完全可以把这些小文件看成一个文件。。
我认为更要考虑的是700M的光盘最好刻到650M,而不是一定要到达700M。
一种现实情况是:一个写在最后的500M文件由于光盘边缘磨损被损坏了,这时候这光盘的有效空间就变成了200M。
并且在实际应用过程中,还要考虑空间的浪费。一个1字节的文件它所占的很可能是4k甚至更多(这和蔟大小有关)。


要谈优化,那必须要把这一堆文件进行系统的分析。
比如:
所有文件的大小和是多少?(比容量M小?)
计划的优秀容量范围M是多少(如:630-670)
每个文件f的大小是多少, 相对'容量范围M'的比是多少?
按照这个容量比(v = f/M)做分类,排序,生成数据列表L。如哪些是0.1级别的,哪些是0.01级别的。。。0.001的小弟弟就可以一箩筐的放一起分析了。

从分类中挑吧:
挑单个一定是效率最高的。二个。。三个就是次之。。。



优化就要看楼主的期望来分析了。
楼主要最优光盘空间:
那只能穷举了。找到+起来是M的话就可以break;

楼主要最优执行性能:
那可以优化,就如我上面说的要定义一个 优秀容量范围M(630-670)
再分析数据列表L来优化程序。很多典型就要做检测了。。
你得到的很可能不是一个 最优光盘空间 的结果。






这种问题单一算法一定没性能可言。


Delix 2007-02-05
  • 打赏
  • 举报
回复
f[n, s]: 在总容量不超过s情况下的,在前n个文件挑出若干个能获得的最大容量

初始状态:f[0, s] = 0 0<s<S
递推关系:f[n, s] = max(f[n-1, s-filesize[n]]) 0<s<S
终止条件:f[N, S]

时间复杂度O(NS),空间复杂度O(S)。
wzhiyuan 2007-02-05
  • 打赏
  • 举报
回复
mark
超级大笨狼 2007-02-05
  • 打赏
  • 举报
回复
Arr=array(350, 200, 80, 300 )


4:300
3:80
2:200
1:350
34:380
24:500
14:650
23:280
13:430
12:550
234:580
134:730
124:850
123:630
1234:930
最优方案:14:650
冰火梦幻 2007-02-04
  • 打赏
  • 举报
回复
如果不要求刚好n个文件,代码少一些
如果刚好n个文件,可以剪枝掉很多
例如,所有文件大小为a0,a1,a2,……,aM,都<=目标容量V
已经按照从小到大排序
之后就是DP了
先放a0
再对于0和a0分别放/不放a1,得到0,a0,a1,a0+a1四个解
再对于上面四个解分别放/不放a2,得到0,a0,a1,a2,a0+a1,a0+a2,a1+a2,a0+a1+a2八个解
……
也就是穷举所有的放/不放的组合
由于总容量V,复杂度为O(V*M)
如果限制了文件数n,当前已经考察了K个文件,对于某个包含k个文件的解s,连续取aK、aK+1、……直到取满n个文件(从文件K到文件K+(n-k-1)的容量和可以事先用O(n^2)得到),再看是否超出了V就可以了,超出的直接放弃
happy8888 2007-02-04
  • 打赏
  • 举报
回复
Mark
加载更多回复(48)

21,886

社区成员

发帖
与我相关
我的任务
社区描述
从PHP安装配置,PHP入门,PHP基础到PHP应用
社区管理员
  • 基础编程社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧