简体   繁体   English

如何将一个数组分成3个部分,每个部分的总和大致相等

[英]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构建三部分版本。

  1. Add to the array a new number equal to one-third of the sum of the original numbers. 将新数字添加到数组中,该数字等于原始数字总和的三分之一。
  2. Split the array into two parts using split2 . 使用split2将数组拆分为两部分。
  3. One part has the number that was added; 一部分有添加的数字; remove it. 去掉它。
  4. Split the other part into two using 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): 我建议的方法如下(代码如下):

  • Create data structures (Collections etc) to represent the number of output parts that you need (in your example 3) 创建数据结构(集合等)以表示您需要的输出部分的数量(在您的示例3中)
  • Sort the input array in descending order. 按降序对输入数组进行排序。
  • Iterate through the elements of the input array and for each value: 迭代输入数组的元素和每个值:
    • pick an output part to place the value in (this should be the output part currently with the lowest overall sum..) 选择一个输出部分来放置值(这应该是当前具有最低总和的输出部分..)
    • add the value to the selected output part 将值添加到选定的输出部分

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.

相关问题 如何将数字分成多个部分,使得结果总和等于输入? - How to divide a number into multiple parts so that the resulting sum is equal to the input? 如何将GridView分为两个相等的部分 - How to divide gridview in two equal parts 在等份中划分列表 - Divide List in Equal Parts 将面积分成相等的部分 - Divide area into equal parts 在C#中将列表列表分成相等的部分 - Divide list of lists into equal parts in C# 将数组划分为列表列表 - Divide array into parts into list of list C#如何将文本文件的行分成两等份,并在两个不同的列表框中显示 - c# how to divide lines of a textfile in two equal parts, and show it in two different listbox 将网格中的第一行划分为四个相等的部分WPF - Divide first row in the grid to four equal parts WPF 在给定每次迭代的常量输入数据的情况下,如何在N个偶数部分中分割FOR循环以进行并行执行? - How to divide FOR loop in N even parts for parallel execution given constant input data for each iteration? 如何进行递归 function 接收数组,找到它的总和以及除以总和的数组中的整数 - How to do a recursion function that receives an array, find the sum of it and the integers in the array that divide the sum
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM