【问题标题】:Best performing algorithm for unique trip selection using arrays?使用数组进行独特行程选择的最佳性能算法?
【发布时间】:2018-10-06 15:56:13
【问题描述】:

如果给我三​​个长度相等的数组。每个阵列代表我在公路旅行中到特定景点的距离(即第一个阵列仅是主题公园,第二个阵列仅是博物馆,第三个是仅海滩)。我不想确定每次旅行中所有可能的旅行都停在每种类型的景点之一,从不倒车,从不两次访问同一个景点。

IE 如果我有以下三个数组: [29 50] [61 37] [37 70]

该函数将返回 3,因为可能的组合为:(29,61,70)(29,37,70)(50,61,70)

到目前为止我得到了什么: 公共 int test(int[] A, int[] B, int[] C) {

    int firstStop = 0;
    int secondStop = 0;
    int thirdStop = 0;

    List<List<int>> possibleCombinations = new List<List<int>>();

    for(int i = 0; i < A.Length; i++)
    {
        firstStop = A[i];
        for(int j = 0; j < B.Length; j++)
        {
           if(firstStop < B[j])
           {
                secondStop = B[j];   
                for(int k = 0; k < C.Length; k++)
                {
                   if(secondStop < C[k])
                   {
                       thirdStop = C[k];
                       possibleCombinations.Add(new List<int>{firstStop, secondStop, thirdStop});
                   }
                }
           }
        }      
    }
    return possibleCombinations.Count();
}

这适用于以下测试用例:

示例测试:([29, 50], [61, 37], [37, 70]) OK 返回 3

示例测试:([5], [5], [5]) OK 返回 0

示例测试:([61, 62], [37, 38], [29, 30]) 失败返回 0

正确计算此值的正确算法是什么? 性能最好的算法是什么?

如何判断这个算法的时间复杂度的表现(即O(N*log(N))?)

【问题讨论】:

    标签: c# algorithm function array-algorithms


    【解决方案1】:

    更新:这个问题已经用新的细节重写,仍然完全不清楚和自相矛盾;试图用原始发布者澄清问题的尝试没有成功,原始发布者承认在自己理解问题之前已经开始编码。下面的解决方案对于最初陈述的问题是正确的;真正问题的解决方案是什么样的,没有人能说,因为没有人能说出真正的问题是什么。出于历史目的,我将把它留在这里。


    让我们重新陈述问题:

    • 我们获得了到沿路景点的三个距离数组。
    • 我们希望列举在不回溯的景点中所有可能的停靠点序列。 (注意:问题的陈述是枚举它们;给定的错误算法计数它们。这些是完全不同的问题。计数它们可能非常快。枚举它们是太慢了!如果问题是计算它们,那么澄清问题。)
    • 问题中没有给出其他约束。 (例如,问题中没有给出我们在不超过一个海滩停留,或者我们必须在每一种海滩停留,或者我们必须在去博物馆之前去海滩。如果这些是那么必须在问题中说明它们

    假设总共有 n 个景点。对于每个景点,我们要么参观,要么不参观。似乎有 2n 种可能性。但是,有一个问题。假设我们有两个博物馆,M1 和 M2 都在 5 公里处。可能的路线是:

    (Start, End)  -- visit no attractions on your road trip
    (Start, M1, End) 
    (Start, M2, End) 
    (Start, M1, M2, End) 
    (Start, M2, M1, End)
    

    有五种非回溯的可能性,而不是四种。

    你想要的算法是:

    • 按距离对景点进行分区,使所有分区包含相同距离的景点。
    • 对于每个分区,在该分区内生成一组所有子集的所有可能排序。不要忘记“跳过所有这些”是一种可能的顺序。
    • 您想要的组合是所有分区排序集的笛卡尔积。

    这应该给你足够的提示来取得进展。这里有几个问题需要解决:分区、在一个分区内排列,然后取任意多个集合的叉积。 我和其他许多人都写过关于所有这些主题的文章,所以如果您不知道如何自己解决这些子问题,请进行一些研究

    关于渐近性能:如上所述,给出的问题是枚举解决方案。 如前所述,最好的情况是 2n 的情况在相同距离没有景点的地方,所以我们至少是指数级的。如果有碰撞,那么它就变成了许多阶乘的产物;我把它留给你解决,但它很大。

    再说一遍:如果问题是要找出解决方案的 number 个,那就容易多了。您不必枚举它们即可知道有多少解决方案!只需计算每个分区的排序数,然后将所有计数相乘。我把计算分区的渐近性能、计算排序数并将它们相乘作为练习。

    【讨论】:

    • 请看一下我上面的 cmets 以及我对这个问题所做的更新。谢谢。
    【解决方案2】:

    您的解决方案在O(n ^ 3) 中运行。但是,如果您需要生成所有可能的组合并且距离按行和列排序,即

    [1, 2, 3] 
    [4, 5, 6]
    [7, 8, 9]
    

    所有解决方案都将降级为O(n^3),因为它需要计算所有个可能的子序列。

    如果输入有大量数据并且它们之间的距离相对较远,那么排序 + 二分查找 + 递归解决方案可能会更快。

    static List<List<int>> answer = new List<List<int>>();
    static void findPaths(List<List<int>> distances, List<int> path, int rowIndex = 0, int previousValue = -1)
    {
        if(rowIndex == distances.Count)
        {
            answer.Add(path);
            return;
        }
        previousValue = previousValue == -1 ? distances[0][0] : previousValue;
        int startIndex = distances[rowIndex].BinarySearch(previousValue);
        startIndex = startIndex < 0 ? Math.Abs(startIndex) - 1 : startIndex;
        // No further destination can be added
        if (startIndex == distances[rowIndex].Count)
            return;
    
        for(int i=startIndex; i < distances[rowIndex].Count; ++i)
        {
            var temp = new List<int>(path);
            int currentValue = distances[rowIndex][i];
            temp.Add(currentValue);
            findPaths(distances, temp, rowIndex + 1, currentValue);
        }
    }
    

    此解决方案的大部分节省来自这样一个事实,即由于数据已经排序,我们无需查看距离小于我们先前值的下一个目的地的距离。

    对于更小和更接近的距离,这可能是一种过度杀伤力,因为额外的排序和二进制搜索开销使其比直接的蛮力方法慢。

    最终我认为这取决于您的数据是怎样的,您可以尝试这两种方法并尝试哪种方法更适合您。

    注意:此解决方案不假设严格增加距离,即)[29, 37, 37] 在此处有效。如果您不想要这样的解决方案,则必须将 Binary Search 更改为执行上限而不是下限。

    【讨论】:

      【解决方案3】:

      对状态使用动态规划。由于只有 3 个数组,所以只有 2*2*2 个状态。

      组合数组并对其进行排序。 [29、37、37、50、61、70]。我们制作了一个二维数组:dp[0..6][0..7]。有8种状态:

      • 001 表示我们选择了第一个数组。
      • 010 表示我们选择了第二个数组。
      • 011 表示我们选择了第一个和第二个数组。
      • .....
      • 111 表示我们选择了第 1、2、3 个数组。

      复杂度是O(n*8)=O(n)

      【讨论】:

      • 这听起来像是解决不同问题的好方法。据我了解,在第一个数组的项的值严格小于第二个数组的项的值的条件下,OP 希望有一个元组列表,每个元组都包含每个原始数组中的一个项,这又必须严格小于第三个数组中项目的值。我不明白,组合和排序如何保证获得每个数组中的一项。 -- 此外,似乎每个数组可能有两个以上的项目,并且可能长度不完全相同。
      猜你喜欢
      • 1970-01-01
      • 2020-09-16
      • 2021-11-01
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      相关资源
      最近更新 更多