一个挺复杂的搜索问题

ShowLovE 2006-01-23 12:01:27
问题:现有8种不同类型的小球, 质量和数量分别如下:
类型 质量(克) 数量(个)
1 34 10
2 46 4
3 85 3
4 128 3
5 142 19
6 194 5
7 215 1
8 350 1
有20相同的盒子, 将这些小球放到这20个盒子内, 每个盒子小球数量只要不超过题中的所有小球的数量和即可, 可以随意放, 但要使得质量方差和最小, 即设每个盒子平均放 A 克,
显然 A = 质量和/20, 即 A = 270左右, 设第 i 个盒子所放小球的质量和为 x(i), 即使得
Z = Sigma( ( x(i) - A )^2 ) 最小.

写了一个随机化算法, 但收敛太快, 与全局最优解的差距挺大的.
...全文
318 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
ShowLovE 2006-02-08
  • 打赏
  • 举报
回复
To: Zephyrzzz() 你得到的结果与我的结果是一样的, 平均本来是269.8, 我用的近似值270.
不知道这个值是不是最优解, 熟悉遗传和蚁群等算法的大侠看看哈, 帮忙算一算, 分不够可以另开帖.
我用的是一般的局部搜索, 收敛很快, 程序只需随机10次. 每次都可以得到11283.2
程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <limits.h>
#include <assert.h>

#define MAXINT 300000000
#define N 8
#define M 50
#define MAX 20

int a[N] = { 34, 46, 85, 128, 142, 194, 215, 350 };
int num[N] = { 10, 4, 3, 3, 19, 5, 1, 1 };
int b[M], n;
double max;
double AVG = 269.8;

typedef struct Node
{
int b[M], num, sum;
}elemType;
elemType rec[MAX], ans[MAX];

int Init();
int print( elemType* );
double result( int, double );
int swap( int&, int& );
double min( double, double, double );
int Rand_Init();

int main()
{
int i, j, k, ix, jx, temp;
double x1, x2, y1, y2, x3, y3, x4, y4;
double sum;
int times, flag = 1;
Init();
times = 10, max = INT_MAX;
srand( time(NULL) );
while( times-- )
{
Rand_Init(); //初始随机化一序列
flag = 1;
while( flag )
{
flag = 0;
for( i = 0; i < MAX; i++ )
for( j = i + 1; j < MAX; j++ )
{
x1 = result( rec[i].sum, AVG ), y1 = result( rec[j].sum, AVG );
for( ix = 0; ix < rec[i].num; ix++ )
for( jx = 0; jx < rec[j].num; jx++ ) //随机化调整: 三种调整方式.
{
x2 = result( rec[i].sum - rec[i].b[ix] + rec[j].b[jx], AVG );
y2 = result( rec[j].sum - rec[j].b[jx] + rec[i].b[ix], AVG );
if( rec[j].num > 1 )
{
x3 = result( rec[i].sum + rec[j].b[jx], AVG );
y3 = result( rec[j].sum - rec[j].b[jx], AVG );
}
else
{ x3 = y3 = MAXINT; }
if( rec[i].num > 1 )
{
x4 = result( rec[i].sum - rec[i].b[ix], AVG );
y4 = result( rec[j].sum + rec[i].b[ix], AVG );
}
else
{ x4 = y4 = MAXINT; }
if( (x1 + y1) > min( (x2+y2), (x3+y3), (x4+y4) ) )
{
if( ( (x2 + y2) <= (x3 + y3) ) && ( (x2 + y2) <= (x4 + y4) ) ) flag = 1;
if( ( (x3 + y3) <= (x2 + y2) ) && ( (x3 + y3) <= (x4 + y4) ) ) flag = 2;
if( ( (x4 + y4) <= (x2 + y2) ) && ( (x4 + y4) <= (x3 + y3) ) ) flag = 3;
//选择不同的调整方式
if( flag == 1 ) //将某两个盒子的元素交换
{
rec[i].sum = rec[i].sum - rec[i].b[ix] + rec[j].b[jx];
rec[j].sum = rec[j].sum - rec[j].b[jx] + rec[i].b[ix];
x1 = x2, y1 = y2;
swap( rec[i].b[ix], rec[j].b[jx] );
}
else if( flag == 2 ) //将某个盒子的元素放到另一个盒子里
{
assert( x3 != MAXINT && y3 != MAXINT );
x1 = x3, y1 = y3;
assert( rec[j].b[jx] != 0 );
rec[i].sum += rec[j].b[jx];
rec[j].sum -= rec[j].b[jx];
rec[i].b[rec[i].num++] = rec[j].b[jx];
for( k = jx; k < rec[j].num; k++ )
rec[j].b[k] = rec[j].b[k+1];
rec[j].num--;
if( jx >= rec[j].num ) break;
}
else
{
x1 = x4, y1 = y4;
assert( rec[i].b[ix] != 0 );
rec[i].sum -= rec[i].b[ix];
rec[j].sum += rec[i].b[ix];
rec[j].b[rec[j].num++] = rec[i].b[ix];
for( k = ix; k < rec[i].num; k++ )
rec[i].b[k] = rec[i].b[k+1];
rec[i].num--;
if( ix >= rec[i].num ) break;
}
}

}
}
}
////////////////////////////////////保存局部最优解
for( k = 0, sum = 0; k < MAX; k++ )
sum += result( rec[k].sum, AVG );
if( sum < max )
{
max = sum;
for( k = 0; k < MAX; k++ )
ans[k] = rec[k];
}
}
print( ans );
printf( "\n%lf\n", max );
return 0;
}

int Init()
{
int i, j, k;
for( i = 0, n = 0; i < N; i++ )
for( j = 0; j < num[i]; j++ )
b[n++] = a[i];
return 0;
}

int Rand_Init()
{
int i, k;
for( i = 0; i < n; i++ )
swap( b[i], b[rand()%n] );
for( i = 0; i < MAX; i++ )
rec[i].num = rec[i].sum = 0;
for( i = 0, k = 0; i < n; i++ )
{
if( k < 6 )
{ if( rec[k].num >= 3 ) k++; }
else
{ if( rec[k].num >= 2 ) k++; }
rec[k].b[rec[k].num++] = b[i];
rec[k].sum += b[i];
}
return 0;
}

int print( elemType p[] )
{
int i, j;
for( i = 0; i < MAX; i++ )
{
for( j = 0; j < p[i].num; j++ )
printf( "%d ", p[i].b[j] );
printf( "\n" );
}
return 0;
}

double result( int x, double y )
{
return ( x - y ) * ( x - y );
}

int swap( int &p, int &q )
{
int temp = p;
p = q;
q = temp;
return 0;
}

double min( double x, double y, double z )
{
return ( ( x > y ) ? ( ( y > z ) ? ( z ) : ( y ) ) : ( ( x > z ) ? ( z ) : ( x ) ) );
}
Zephyrzzz 2006-02-02
  • 打赏
  • 举报
回复
用简单的模拟退火算法算了一下,最优解为11283.2应该没有错.顺便贴个程序大家参考一下,呵呵.
初始参数设置也许能调试得更好一些,运行速度并不是很快,不过30%左右能出最优解,其余的很接近该解,用遗传算法效率可能高些.
编码用每个小球放入盒子编号表示,生成邻域方法发现用同时修改两个小球所放盒子的编号能很好的收敛,只修改一个球则效果很差.
程序输出46个球对应盒子的编号.以下为一组解:
11283.200000
34:7 34:12 34:0 34:4 34:14 34:19 34:19 34:7 34:0 34:17
46:10 46:8 46:1 46:19 85:4 85:12 85:14 128:15 128:9 128:13
142:18 142:9 142:18 142:13 142:3 142:5 142:11 142:4 142:12 142:2
142:6 142:2 142:5 142:3 142:15 142:6 142:11 142:14 142:19 194:10
194:1 194:7 194:8 194:0 215:17 350:16

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

const double T=500;
const double alpha=0.98;
const double over=1e-5;
const int maxloop=800;
const int n=8;
const int len=46;
const int box=20;
int ball[n][2]={{34,10},{46,4},{85,3},{128,3},{142,19},{194,5},{215,1},{350,1}};
int a[len];
double A;

void initialize()
{
int i,t=0;
for (i=0;i<len;i++)
a[i]=rand()%box;
for (i=0;i<n;i++)
t+=ball[i][0]*ball[i][1];
A=(double)t/box;
}

double func(int a[])
{
int i,j,k=0,x[box];
memset(x,0,sizeof(x));
for (i=0;i<n;i++)
for (j=0;j<ball[i][1];j++)
x[a[k++]]+=ball[i][0];
double Z=0;
for (i=0;i<box;i++)
Z+=(x[i]-A)*(x[i]-A);
return Z;
}

double random()
{
return rand()/32767.;
}

void work()
{
int loop;
int b[len];
double t=T,P,fi,fj;
fi=func(a);
while (t>=over) {
for (loop=0;loop<maxloop;loop++)
{
memcpy(b,a,sizeof(int)*len);
b[rand()%len]=rand()%box;
b[rand()%len]=rand()%box;
fj=func(b);
if (fj<fi) fi=fj,memcpy(a,b,sizeof(b));
else {
P=exp((fi-fj)/t);
if (P>random()) fi=fj,memcpy(a,b,sizeof(b));
}
}
t*=alpha;
}
}

void output()
{
printf("%lf\n",func(a));
int i,j,k=0;
for (i=0;i<n;i++)
for (j=0;j<ball[i][1];j++)
printf("%d:%d\t",ball[i][0],a[k++]);
printf("\n");
}

void main()
{
srand(time(NULL));
initialize();
work();
output();
}

billjeff 2006-01-30
  • 打赏
  • 举报
回复
穷举行不通吧,20个箱子。是不是要考虑一些方差的一些性质,先MARK下,再看看
sailor_Song 2006-01-26
  • 打赏
  • 举报
回复
穷举是不是可以呢?
ShowLovE 2006-01-25
  • 打赏
  • 举报
回复
加一个条件: 每个盒子至少有一个球, 感觉这样要简单些.
下面是我得的一组结果, 不知道是不是最优:
46 142 34 34
142 85 34
142 142
350
128 142
142 128
194 34 34
142 142
128 142
34 194 34
142 142
85 142 34
194 46
194 46
142 142
194 46
142 142
215 34
142 142
85 142 34

Z = 11284
sjjf 2006-01-24
  • 打赏
  • 举报
回复
mark
Eddie005 2006-01-23
  • 打赏
  • 举报
回复
up~

33,007

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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