【问题标题】:Generalized Target Sum Problem广义目标和问题
【发布时间】:2011-08-04 07:20:15
【问题描述】:

给定一个整数 x 和一个未排序的整数数组,描述一个算法 确定两个数字加起来是否等于 x。 (在这种情况下,不允许使用哈希表)。 解决办法是:

对数组进行排序。然后,跟踪数组中的两个指针,一个在 开头和结尾。每当当前两个整数之和为 小于 x,向前移动第一个指针,并且每当总和大于 x 时, 向后移动第二个指针。如果你找不到两个加到 x 上的数字 在其中一个指针相遇之前,不存在总和为 x 的整数对。这个 解决方案需要 O(n log n) 时间,因为我们对数字进行排序。

我们能否为k 整数提供通用解决方案。说上面的问题是k=2。我现在想找到3 整数和target sum 等等。

【问题讨论】:

  • 你想用 k 指针进行泛化还是我们可以使用其他结构?
  • 任何其他结构或算法都可以。
  • 顺便说一句,这被称为子集和问题
  • @Hunter:对于固定的 k
  • @Patrick 有点不同,是的,子集和问题对所有 n 个整数都有完整的解决方案。但是,对于任何 k

标签: algorithm data-structures


【解决方案1】:

只是设置一个上限...对于固定的 k,问题总是在 P 中(关于数字列表大小的多项式,无论如何,n)并且可以通过简单的 O( n^k) 算法:生成 k-sets(其中有 C(n,k) 个)并检查它们。对于 k=2 的情况,这对应于生成所有 n(n+1)/2 个二集并检查它们。

如果我们取而代之的是 k

请注意,这些代表了复杂性的严格上限...对于 k = 2,您已经找到了一个 O(n log n) 算法,并且该方法可以推广到更高的 k。

Edit2:删除了一些更严格的界限,因为我的构造似乎是错误的。很抱歉搞砸了。 spin_plate 的道具让我诚实。

【讨论】:

  • 如果你递归地钻研那对,它必须小于或等于总和。只有当这对导致总和更大时,您才会尝试将最后一个指针向后移动...否则,将较低的指针向前移动似乎就足够了。
  • k = 5 和 [1,2,3,4,5.5,6] 和是 15.5 的情况呢?我不明白当小数可以更大或更小时,您如何跟踪要走的路。你能展示一些递归函数的伪代码吗?
  • 稍后我会尝试添加一些,但想法很简单:如果当前 sum 目标,则递减高指针。重复直到解决方案或低指针=高指针。换句话说,要更改哪个指针的决定不取决于递归树的较低层......较低层影响较高层的唯一方法是在找到任何解决方案后完全终止计算。
  • 因此,在我的示例中,我们首先选择 (1,6),然后继续在数组 [2,3,4,5.5] 上查找总和 9。在递归的顶层,总和永远不会太高,所以总是选择 6 而我们找不到答案?我的意思是,我认为您最终将不得不枚举所有(n 选择 k)子集,所以我不知道您的界限是否成立。
  • 有趣。稍后我会看看这是否有问题,如果有,是否可以解决...
【解决方案2】:

我感觉这会引起大量的反对,因为我不会说数学(或 CS 术语),但我只是在表达我对如何扩展您的方法的想法
我假设

  1. 需要一组不同的整数(不重复)
  2. 每当 sum=x 我存储满足 sum=x 的整数并继续前进,所以 我得到所有满足 sum=x 的整数元组

A 部分

k = 3 和 x = 总和

按升序对数组进行排序。
将 first_pointer 放在第一个整数上,将 second_pointer 放在第二个整数上(即在第一个指针旁边)。
将第三个指针放在最后一个整数上。

  1. 计算总和=first_pointer + second_pointer + third_pointer。
  2. 如果 sum
  3. if sum > x thenthird_pointer--重复步骤1。
  4. 重复步骤 1 到 3 直到 second_pointer >= third_pointer,即指针相遇。
  5. first_pointer++; second_pointer=first_pointer+1; third_pointer=last integer 即将 first_pointer 移动一步(在右侧)并将 second_pointer 放在第一个指针旁边,将第三个指针返回到最后一个整数。
  6. 重复第 1 步到第 5 步,直到第 5 步 second_pointer=third_pointer。
  7. 如果你到达这里,没有找到任何 sum=x 则没有解决方案(我可能在这里错了,但我看不到任何其他可能性。)


B 部分

对于 k = 4 和 x = sum

按升序对数组进行排序。
将前两个指针放在 A 部分中。
将第三个和第四个放在最后两个整数上,即第三个指针=second_last_integer 和第四个指针=last_integer。

  1. 类似于Section A Step 1 sum=pointer(1st+2nd+3rd+4th)
  2. 类似于 A 部分第 2 步
  3. (这是不同的)如果 sum > x then Fourth_pointer--
  4. 类似于第 A 部分第 4 步
  5. 现在完全不同了)为了清楚起见,我将其分为 5.a 和 5.b
    5.a 这部分类似于Section A,您将移动第一个和第二个指针,使它们彼此相邻并重复Section B的步骤1到4 >
    5.b 如果还没有找到总和fourth_pointer--; third_pointer=fourth_pointer-1 并重复步骤 1 到 4 和 5.a
  6. 重复 5.b 直到 second_pointer=third_pointer。

如果你已经到达这里而没有任何 sum=x,那么就没有解决方案 //standard-disclaimer


C 部分

(我的屏幕空间真的太小了,因为我看不到我正在写的内容的预览。所以我会保持简短,让你自己想象)
对于任何 k 和 x = sum

按升序对数组进行排序。
将第一个 int(k/2) 指针放在开头。
将其余的 k-int(k/2) 指针放在最后。

  1. 计算总和 = sigma(k)。
  2. 如果总和
  3. 如果 sum > x then pointer(k - int(k / 2))--.从第 1 步开始重复。
  4. 从步骤 1 重复直到指针(int(k / 2)) >= 指针(k - int(k / 2))。
  5. 现在获取 [int(k / 2) - 1,int(k / 2),(k - int(k / 2)),(k - int(k / 2) + 1)] 指针并继续类似于 B 部分
  6. 重复上述操作,将 int(k / 2) - 2 向右移动,然后将 (k - int(k / 2) + 2) 向左移动。
  7. 扩展第 6 步,直到你移动所有指针

结束

//呼!!写了这么多!!

【讨论】:

  • link 对于 pastebin,您可以在没有滚动条的情况下阅读它,我很长时间无法发布,因为我认为我的帖子中有很多代码,但我做到了没有正确缩进。我真诚地希望没有人要求我计算我的算法的顺序手指交叉
  • @downvoter :我期待这个,但请告诉我原因??
  • @Sudhi:请检查您的 A 部分 - 它不可读。我理解你的想法,这似乎是有道理的,但没有复杂性分析,甚至没有清晰的算法,它是完全没用的。
  • 第 2 步应为: 2.if sum<x then second_pointer++ 从第 1 步重复。3. 如果总和....@ffriend:现在清楚了吗?抱歉,我在算法分析方面没有相应的专业知识,但我相信我的算法足够清晰,您不了解算法的哪一部分?
  • @glenn-jackman : 你能重新编辑我的帖子吗? A 节和 B 节中的第 2 步缺少完整的 if sum<x then... 语句。我试过了,但仍然抱怨没有缩进我的代码:(
【解决方案3】:

如果您可以使用任何数据结构,则可以简单地将其视为knapsack problem,并跟踪除总和之外您还使用了多少数字。

numbers = [xxx]
buckets = [[0,0] for x in range(MAX_SUM)]
buckets[0][0] = 1;
for number in numbers:
    for bucketi in range(MAX_SUM):
        if buckets[bucketi][0] == 1 and buckets[bucketi][1] < k: 
           buckets[bucketi+number][0] = 1;
           buckets[bucketi+number][1] = buckets[bucketi][1] + 1;

这也是@Patrick 的尝试,我不确定界限,但这是一个有趣的想法。

def go_(numbers, range_bottom,range_top,sum_target,k,min_v,max_v,nums):
    min_v_,max_v_,res_ = go(numbers, range_bottom,range_top,sum_target-sum(nums),k)
    min_v = min(min_v,min_v_)
    max_v = max(max_v,max_v_)
    if len(res_) > 0:
        newres = [x for x in res_]
        newres = newres+nums
        return [0,0,newres]
    return [min_v,max_v,res_]

def go(numbers, range_bottom,range_top,sum_target,k):

    if sum_target==0 and k == 0:
        return [0,0,['-']];
    elif sum_target<0 or k==0 or range_bottom == range_top:
        return [sum_target,sum_target,[]]


    min_v = 666; max_v = -666;

    if range_top-range_bottom>1:
        min_v ,max_v, res_ = go_(numbers, range_bottom+1, range_top-1, sum_target,k-2,min_v,max_v,[numbers[range_bottom],numbers[range_top-1\
]])
        if len(res_):

            return [0,0,res_];

    if not ( min_v<0 and max_v<0 ):
        min_v ,max_v, res_ = go_(numbers, range_bottom+1, range_top, sum_target ,k,min_v,max_v ,[])
        if len(res_):


            return [0,0,res_];


    if not ( min_v>0 and max_v>0 ):
        min_v ,max_v, res_ = go_(numbers, range_bottom, range_top-1, sum_target,k,min_v,max_v,[])
        if len(res_):

            return [0,0,res_];

    return [min_v,max_v,res_]

【讨论】:

    猜你喜欢
    • 2011-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-27
    • 2012-09-13
    • 1970-01-01
    相关资源
    最近更新 更多