一次两次递归添加数组中元素的最小和

时间:2019-07-10 21:04:29

标签: c# algorithm recursion data-structures recursive-datastructures

我有一个整数数组。每个元素的值表示处理文件所花费的时间。文件的处理包括一次合并两个文件。寻找可用于处理所有文件的最短时间的算法是什么?例如。 -{3,5,9,12,14,18}。

处理时间可以计算为- 情况1)-

@Binding

所以处理的总时间为61 + 35 + 26 + 17 + 8 = 147

情况2)-

Text

这次总时间为61 + 40 + 23 + 17 + 21 = 162

在我看来,如情况1所示,连续排序数组并添加最少两个元素是最小的最佳选择。我的逻辑对吗?如果不是,那么以最佳性能实现这一目标的正确,最简单的方法是什么?

4 个答案:

答案 0 :(得分:2)

有了排序列表后,由于您只删除了两个最小项并用一个替换,因此更有意义的是进行排序插入并将新项放置在正确的位置,而不是对整个项重新排序清单。但是,这只节省了少量时间-快了约1%。

我的方法CostOfMerge不假定输入为List,但如果是,则可以删除转换ToList步骤。

public static class IEnumerableExt {
    public static int CostOfMerge(this IEnumerable<int> psrc) {
        var src = psrc.ToList();
        src.Sort();
        while (src.Count > 1) {
            var sum = src[0]+src[1];
            src.RemoveRange(0, 2);

            var index = src.BinarySearch(sum);
            if (index < 0)
                index = ~index;
            src.Insert(index, sum);

            total += sum;
        }
        return total;
    }
}

答案 1 :(得分:2)

正如在其他答案中已经讨论的那样,最好的策略将是始终以最小的每次迭代成本在两个项目上工作。因此,唯一剩下的问题是如何有效地每次取出两个最小的物品。

由于您要求获得最佳性能,因此我毫不客气地从NetMage中提取了该算法,并对其进行了修改,以使其在我的测试用例中可以提高大约40%的速度(感谢和NetMage的+1)。

这个想法主要是在单个阵列上就地工作。 每次迭代都会将起始索引增加1,并移动数组中的元素以为当前迭代的总和腾出空间。

public static long CostOfMerge2(this IEnumerable<int> psrc)
{
    long total = 0;

    var src = psrc.ToArray();
    Array.Sort(src);

    var i = 1;
    int length = src.Length;
    while (i < length)
    {
        var sum = src[i - 1] + src[i];

        total += sum;

        // find insert position for sum
        var index = Array.BinarySearch(src, i + 1, length - i - 1, sum);
        if (index < 0)
            index = ~index;
        --index;

        // shift items that come before insert position one place to the left
        if (i < index)
            Array.Copy(src, i + 1, src, i, index - i);

        src[index] = sum;

        ++i;
    }

    return total;
}

我用以下调用代码(在CostOfMergeCostOfMerge2之间切换)进行了测试,并为随机种子,元素数量和初始项目的最大值设置了一些不同的值。

static void Main(string[] args)
{
    var r = new Random(10);

    var testcase = Enumerable.Range(0, 400000).Select(x => r.Next(1000)).ToList();
    var sw = Stopwatch.StartNew();
    long resultCost = testcase.CostOfMerge();
    sw.Stop();
    Console.WriteLine($"Cost of Merge: {resultCost}");
    Console.WriteLine($"Time of Merge: {sw.Elapsed}");
    Console.ReadLine();
}

显示的NetMage CostOfMerge配置的结果:

Cost of Merge: 3670570720
Time of Merge: 00:00:15.4472251

我的CostOfMerge2:

Cost of Merge: 3670570720
Time of Merge: 00:00:08.7193612

当然,详细的数字取决于硬件,并且根据负载的不同,差异可能更大或更小。

答案 2 :(得分:1)

否,这是多相合并的最小值:N是带宽(可以同时合并的文件数),然后您希望在每一步合并最小的(N-1)个文件。但是,由于存在这个更普遍的问题,您希望尽可能长的时间延迟较大的文件-您可能希望提前一到两个步骤合并少于(N-1)个文件,这有点像消除中的“再见”比赛。您希望后面的所有步骤都包含完整的(N-1)个文件。

例如,给定N = 4和文件1, 6, 7, 8, 14, 22

早期合并:

[22], 14, 22
[58]
total = 80

后期合并:

[14], 8, 14, 22
[58]
total = 72

答案 3 :(得分:1)

在这里,您可以应用以下逻辑来获得所需的输出。

  1. 从列表中获取前两个最小值。
  2. 从列表中删除前两个最小值。
  3. 追加列表中前两个最小值之和
  4. 并继续,直到列表的大小变为1
  5. 返回列表中的唯一元素。也就是说,这是您处理每个项目所需的最短时间。

如果发现有帮助的.. :)

,您可以按照我的Java代码进行操作。
public class MinimumSums {
   private static Integer getFirstMinimum(ArrayList<Integer> list) {
    Integer min = Integer.MAX_VALUE;

    for(int i=0; i<list.size(); i++) {
        if(list.get(i) <= min)
            min = list.get(i);
    }

    return min;
}

private static Integer getSecondMinimum(ArrayList<Integer> list, Integer firstItem) {
    Integer min = Integer.MAX_VALUE;

    for(int i=0; i<list.size(); i++) {
        if(list.get(i) <= min && list.get(i)> firstItem)
            min = list.get(i);
    }
    return min;
}
public static void main(String[] args) {
    Integer[] processes = {5, 9, 3, 14, 12, 18};

    ArrayList<Integer> list = new ArrayList<Integer>();
    ArrayList<Integer> temp = new ArrayList<Integer>();

    list.addAll(Arrays.asList(processes));

    while(list.size()!= 1) {
        Integer firstMin = getFirstMinimum(list); // getting first min value
        Integer secondMin = getSecondMinimum(list, firstMin); // getting second min

        list.remove(firstMin);
        list.remove(secondMin);

        list.add(firstMin+secondMin);
        temp.add(firstMin + secondMin);
    }

    System.out.println(temp); // prints all the minimum pairs.. 
    System.out.println(list.get(0)); // prints the output

}

}