求教一个按最贴近比例分配数值的算法

暗石绿 2008-11-17 03:14:31
我有这么一组数值:
序号 数值
1 100
2 200
3 300
4 400
5 500
6 600
7 700
8 600

这样,这8个数值的total,就应该是3400。
它们需要被分成三部分。
我如果按 1:3:4这么分配,肯定会分配不均。
按最贴近比例的分配原则,它们可能会是以下的情况:
(序号/数值)
第一部分:4/400
第二部分:7/700,8/600
第三部分:1/100,2/200,3/300,5/500,6/600

第一部分:第二部分:第三部分 ---- 400:1300:1700

由于以上算出来的情况,是由人工来判断的。
有能帮助用程序来判断实现的吗?

谢谢。
300分酬谢。
...全文
1475 42 打赏 收藏 转发到动态 举报
写回复
用AI写文章
42 条回复
切换为时间正序
请发表友善的回复…
发表回复
yoursWTR 2008-11-18
  • 打赏
  • 举报
回复
[Quote=引用楼主 xrascal 的帖子:]
我有这么一组数值:
序号 数值
1 100
2 200
3 300
4 400
5 500
6 600
7 700
8 600

这样,这8个数值的total,就应该是3400。
它们需要被分成三部分。
我如果按 1:3:4这么分配,肯定会分配不均。
按最贴近比例的分配原则,它们可能会是以下的情况:
(序号/数值)
第一部分:4/400
第二部分:7/700,8/600
第三部分:1/100,2/20…
[/Quote]

比如 你有N个数 他们的比例是1:2:3 那么
第一列就该是0到 ~ n/6
第二列就是 n/6 ~ 2n/6
第三列就是 2n/6 ~ n

其中只需要注意 整除 和 连续相同的元素处理
mengxj85 2008-11-18
  • 打赏
  • 举报
回复
Mark
mjjzg 2008-11-18
  • 打赏
  • 举报
回复
up.学习一下
fellowcheng 2008-11-18
  • 打赏
  • 举报
回复
mark
RexZheng 2008-11-18
  • 打赏
  • 举报
回复

offset 可证明在 0 到 maxValue 之间

RexZheng 2008-11-18
  • 打赏
  • 举报
回复
运算结果
Offset: 25
X: 400
Y: 200 500 600
Z: 100 300 600 700
RexZheng 2008-11-18
  • 打赏
  • 举报
回复

代码写得不是很好看,而且还有一些可以优化的地方。



static void Main()
{
int[] values = { 100, 200, 300, 400, 500, 600, 700, 600 };
//int[] values = { 1, 1, 1 , 1,2};
double x = 1, y = 3, z = 4;

int maxValue = 0;
int sumTotal = 0;
foreach (int val in values)
{
sumTotal += val;
if (val > maxValue) maxValue = val;
}

int len = values.Length;
double xyz = x + y + z;
x = (x * sumTotal) / xyz;
y = (y * sumTotal) / xyz;
z = (z * sumTotal) / xyz;

double offset = maxValue;
int[] x1 = new int[0], y1 = new int[0], z1 = new int[0];

for (int countX = 1; countX <= len - 2; countX++)
{
Combination(len, countX, delegate(int[] indexesX)
{
int sumX = Sum(values, indexesX);
double offsetX = Math.Abs(sumX - x);
if (offsetX <= maxValue)
{
int[] arrayYZ = CopyArrayByExclude(values, indexesX);
int lenYZ = arrayYZ.Length;
for (int countY = 1; countY <= lenYZ - 1; countY++)
{
Combination(lenYZ, countY, delegate(int[] indexesY)
{
int sum2 = Sum(values, indexesY);
double offsetY = Math.Abs(sum2 - y);
if (offsetY <= maxValue)
{
int[] arrayZ = CopyArrayByExclude(arrayYZ, indexesY);
int sumZ = Sum(arrayZ);
double offsetZ = Math.Abs(sumZ - z);
if ((offsetX + offsetY + offsetZ) / 2 <= offset)
{
offset = (offsetX + offsetY + offsetZ) / 2;

x1 = CopyArrayByInclude(values, indexesX);
y1 = CopyArrayByInclude(arrayYZ, indexesY);
z1 = arrayZ;
}
}
});
}
}
});
}

Console.WriteLine("运算结果");
Console.Write("Offset: " + offset);
Console.Write("\nX: ");
foreach (int i in x1) Console.Write(i + " ");
Console.Write("\nY: ");
foreach (int i in y1) Console.Write(i + " ");
Console.Write("\nZ: ");
foreach (int i in z1) Console.Write(i + " ");
Console.ReadKey();
}

static int Sum(int[] values)
{
int total = 0;
for (int i = 0; i < values.Length; i++)
{
total += values[i];
}
return total;
}

static int Sum(int[] values, int[] sumIndexs)
{
int total = 0;
for (int i = 0; i < sumIndexs.Length; i++)
{
total += values[sumIndexs[i]];
}
return total;
}

static int[] CopyArrayByExclude(int[] array, int[] excludeIndexes)
{
int[] newArray = new int[array.Length - excludeIndexes.Length];
int newArrayIndex = 0;
for (int i = 0; i < array.Length; i++)
{
bool pass = false;
foreach (int index in excludeIndexes)
{
if (i == index)
{
pass = true;
break;
}
}
if (!pass)
{
newArray[newArrayIndex] = array[i];
newArrayIndex++;
}
}
return newArray;
}

static int[] CopyArrayByInclude(int[] array, int[] includeIndexes)
{
int[] newArray = new int[includeIndexes.Length];
for (int i = 0; i < includeIndexes.Length; i++)
{
newArray[i] = array[includeIndexes[i]];
}
return newArray;
}

static void Combination(int len, int count, Action<int[]> process)
{
int[] indexes = new int[count];
int pos = 0;
while (true)
{
if (pos == count - 1)
{
process(indexes);

if (indexes[pos] < len - 1)
{
indexes[pos]++;
}
else
{
bool isEnd = true;
for (int i = count - 2; i >= 0; i--)
{
if (indexes[i] < len - count + i)
{
pos = i;
indexes[pos]++;
isEnd = false;
break;
}
}
if (isEnd) break;
}
}
else
{
indexes[pos + 1] = indexes[pos] + 1;
pos++;
}
}
}

暗石绿 2008-11-18
  • 打赏
  • 举报
回复
顶一下。
a12321321321312321 2008-11-17
  • 打赏
  • 举报
回复
分没楼主高,所以楼主问的问题不知道,呵呵。
RexZheng 2008-11-17
  • 打赏
  • 举报
回复
offset >=0 && offset <= min
这个结论是错的
fffff_1982 2008-11-17
  • 打赏
  • 举报
回复
想了一下,最少有 8*35 中算法,相当难,
RexZheng 2008-11-17
  • 打赏
  • 举报
回复
设数值列表中的最小数为 min,则
offset >=0 && offset <= min
这个是可以证明的,现在没时间下班了。

沿着这思路,可以大大地减小计算量。明天再来 :)

暗石绿 2008-11-17
  • 打赏
  • 举报
回复
谢谢009的解释。
我觉得我解释得都没有这么好,只是不能以100这么准确的数来表达。
暗石绿 2008-11-17
  • 打赏
  • 举报
回复
offset 是需要最小,但 offset <= 100 ,这个不一定是100,谁也不知道这个offset会小到什么程度。
也有0的可能。
RexZheng 2008-11-17
  • 打赏
  • 举报
回复

这个最接近值应该这样表述:
设目标比例为 x:y:z (已使用total放大过,不是最简公约数)
实际比例为 x1:y1:z1
偏移量 offset = (abs(x-x1) + abs(y-y1) + abs(z-z1))/2

我们要的结果便是使 offset 最小,而且很容易用反证法证明 offset 一定小于或等于 数值列表中的最小值,楼主的例子中,便是 offset<=100

RexZheng 2008-11-17
  • 打赏
  • 举报
回复

这个最接近值应该这样表述:
设目标比例为 x:y:z (已使用total放大过,不是最简公约数)
实际比例为 x1:y1:z1
偏移量 offset = (abs(x-x1) + abs(y-y1) + abs(z-z1))/2

我们要的结果便是使 offset 最小,而且很容易用反证法证明 offset 一定小于或等于 数值列表中的最小值,楼主的例子中,便是 offset<=100

暗石绿 2008-11-17
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 hornbills 的回复:]

这个解释有些问题,你的意思是|3-1.5| < |3-5| 吗?


是否能这样理解?
把目标比例变换为
1:x:y
实际比例变换为
1:x1:y1

|x1-x|+|y1-y|最小?


如果这样也有问题,比例是乘除关系,如果用差的绝对值,绝对值差小,但是比例差距巨大
比如0.001和0.1差距很小,但是比例差距…
[/Quote]
不使用差的绝对值比较,按乘除关系比较,也就是你说的比例差距。
lfzpf 2008-11-17
  • 打赏
  • 举报
回复
继续关注!
hornbills 2008-11-17
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 xrascal 的回复:]
回15楼:

1 -- 2:2:3
2 -- 1:1:5

3/2 = 1.5
5/1 = 5

1.5 < 5,答案1最贴近。
[/Quote]

这个解释有些问题,你的意思是|3-1.5| < |3-5| 吗?


是否能这样理解?
把目标比例变换为
1:x:y
实际比例变换为
1:x1:y1

|x1-x|+|y1-y|最小?


如果这样也有问题,比例是乘除关系,如果用差的绝对值,绝对值差小,但是比例差距巨大
比如0.001和0.1差距很小,但是比例差距巨大,100和80差距很大,但是比例差距很小。

单线程加锁 2008-11-17
  • 打赏
  • 举报
回复
算出比例,比例基数相加,除以8.
再取最相近的.
我上面的代码就是算比例的.
加载更多回复(22)

62,017

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

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