[英]How to divide an array into 3 parts with the sum of each part roughly equal
I have an arranged array and I want to divide it into 3 parts so that their sum are closest to each other. 我有一个排列的数组,我想将它分成3个部分,以便它们的总和彼此最接近。
Ex: I have this array: 10, 8, 8, 7, 6, 6, 6, 5 so it'll be divided into 3 part like: p1 {10,8} sum = 18 p2 {8,7,6} sum = 21 p3 {6,6,5} sum = 17
The original poster already has a working solution (noted in comments) to split the array into two parts with equal sums; 原始海报已经有一个工作解决方案(在评论中注明)将数组分成两部分,数额相等; call this
split2
. 叫这个
split2
。 The three-part version can be constructed using split2
. 可以使用
split2
构建三部分版本。
split2
. split2
将数组拆分为两部分。 split2
. split2
将另一部分拆分为两部分。 这就像是NP-Hard的two-Partition
问题,但不是强烈的意义,你可以有一个O(nK)算法,其中K是你的输入和的大小,参见pseudo polynomial time algorithm for subset sum
,另见回答divide-list-in-two-parts-that-their-sum-closest-to-each-other
,但在你的情况下,你应该只添加另一个维度来处理它。
Try the following code 请尝试以下代码
int total = 0, partSum = 0, partIndex = 0;
int noOfParts = 3; //Initialize the no. of parts
int[] input = { 10, 8, 8, 7, 6, 6, 6, 5 };
int[] result = new int[noOfParts]; //Initialize result array with no. of locations equal to no. of parts, to store partSums
foreach (int i in input) //Calculate the total of input array values
{
total += i;
}
int threshold = (total / noOfParts) - (total / input.Length) / 2; //Calculate a minimum threshold value for partSum
for (int j = input.Length - 1; j > -1; j--)
{
partSum += input[j]; //Add array values to partSum incrementally
if (partSum >= threshold) //If partSum reaches the threshold value, add it to result[] and reset partSum
{
result[partIndex] = partSum;
partIndex += 1;
partSum = 0;
continue;
}
}
if (partIndex < noOfParts) //If no. of parts in result[] is less than the no. of parts required, add the remaining partSum value
{
result[partIndex] = partSum;
}
Array.Reverse(result);
foreach (int k in result)
{
Console.WriteLine(k);
}
Console.Read();
I've tested this with various values in array(arranged in descending order) and also with different value for no. 我已经使用数组中的各种值(按降序排列)测试了这个值,并且对于no也使用了不同的值。 of parts(3,4,5...) and got good results.
零件(3,4,5 ......)并取得了良好的效果。
// calculate total
total = 0;
for(i = 0; i != size; ++i) {
total += array[i];
}
// partition
n_partitions = 3;
current_partition = 1;
subtotal = array[0];
for(i = 1; i != size; ++i) {
if(subtotal + array[i] > total / n_partitions) {
// start new partition;
current_partition++;
subtotal = array[i];
} else {
// push to current partition
subtotal += array[i];
}
}
Updated with code: 更新代码:
The approach I suggest is as follows (with code below): 我建议的方法如下(代码如下):
With the above logic, you will always be adding to the output part with the lowest overall value (which will help to keep the parts of similar overall value). 使用上述逻辑,您将始终添加具有最低总值的输出部分(这将有助于保持具有相似整体值的部分)。
(in the code sample below I have skipped the array sorting step as your example is already sorted) (在下面的代码示例中,我跳过了数组排序步骤,因为您的示例已经排序)
Code: 码:
// the input array
int[] inputArray = new int[] { 10, 8, 8, 7, 6, 6, 6, 5 };
// the number of parts you want
int numberOfOutputParts = 3;
// create the part structures
List<Part> listOfParts = new List<Part>();
for(int i =0; i < numberOfOutputParts; i++)
{
listOfParts.Add(new Part());
}
// iterate through each input value
foreach (int value in inputArray)
{
// find the part with the lowest sum
int? lowestSumFoundSoFar = null;
Part lowestValuePartSoFar = null;
foreach(Part partToCheck in listOfParts)
{
if (lowestSumFoundSoFar == null || partToCheck.CurrentSum < lowestSumFoundSoFar)
{
lowestSumFoundSoFar = partToCheck.CurrentSum;
lowestValuePartSoFar = partToCheck;
}
}
// add the value to that Part
lowestValuePartSoFar.AddValue(value);
}
The code for the Part class used above (although you could use something better is as follows): 上面使用的Part类的代码(尽管你可以使用更好的东西如下):
public class Part
{
public List<int> Values
{
get;
set;
}
public int CurrentSum
{
get;
set;
}
/// <summary>
/// Default Constructpr
/// </summary>
public Part()
{
Values = new List<int>();
}
public void AddValue(int value)
{
Values.Add(value);
CurrentSum += value;
}
}
Could you try my sample, this may help you 你可以试试我的样品吗,这对你有所帮助
My Algorithm: 1/ Calculate avg value of the array numbers by the number of output array (exp:value=3 in your post) 我的算法:1 /通过输出数组的数量计算数组的avg值(exp:value = 3在你的帖子中)
2/ Sum the array numbers until the Sum has min gap compare to the avg value (calculated in 1/) 2 /对数组求和,直到Sum的最小间隙与avg值进行比较(以1 /计算)
3/ Do step 2 until you go to the end of the array numbers 3 /执行步骤2直到您到达数组编号的末尾
I using C# 3.5 to test 我使用C#3.5进行测试
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace WindowsFormsApplication2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
ArrayList inputValue = new ArrayList();
int avgValue = 0;
bool isFinish = false;
private void button1_Click(object sender, EventArgs e)
{
#region Init data
isFinish = false;
avgValue = 0;
inputValue.Clear();
listBox1.Items.Clear();
//assum you input valid number without space and in desc sorting order
string[] arrNumber = textBox1.Text.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
int numberOfBreak = 3;
int record = Convert.ToInt32(arrNumber[0]);//update the record with the maximum value of the array numbers
for (int i = 0; i < arrNumber.Length; i++)
{
inputValue.Add(Convert.ToInt32(arrNumber[i]));
}
foreach (object obj in inputValue)
{
avgValue += (int)obj;
}
avgValue = avgValue / numberOfBreak;
#endregion
int lastIndex = 0;
while (!isFinish)
{
int index = GetIndex(lastIndex);
string sResult = "";
for (int i = lastIndex; i <= index; i++)
{
sResult += inputValue[i].ToString() + "-";
}
listBox1.Items.Add(sResult);
if (index + 1 < inputValue.Count)
{
lastIndex = index + 1;
}
sResult = "";
}
}
private int GetIndex(int startIndex)
{
int index = -1;
int gap1 = Math.Abs(avgValue - (int)inputValue[startIndex]);
int tempSum = (int)inputValue[startIndex];
if (startIndex < inputValue.Count - 1)
{
int gap2 = 0;
while (gap1 > gap2 && !isFinish)
{
for (int i = startIndex + 1; i < inputValue.Count; i++)
{
tempSum += (int)inputValue[i];
gap2 = Math.Abs(avgValue - tempSum);
if (gap2 <= gap1)
{
gap1 = gap2;
gap2 = 0;
index = i;
if (startIndex <= inputValue.Count - 1)
{
startIndex += 1;
}
else
{
isFinish = true;
}
if (startIndex == inputValue.Count - 1)
{
index = startIndex;
isFinish = true;
}
break;
}
else
{
index = i - 1;
break;
}
}
}
}
else if (startIndex == inputValue.Count - 1)
{
index = startIndex;
isFinish = true;
}
else
{
isFinish = true;
}
return index;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.