【问题标题】:How to create a list of numbers that add up to a specific number如何创建加起来为特定数字的数字列表
【发布时间】:2011-07-26 04:21:24
【问题描述】:

我需要一些帮助,在 Prolog 中编写一个谓词,给定一个数字作为输入,返回一个列表列表,其中的数字相加。

让我们调用谓词 addUpList/2,它应该是这样工作的:

?- addUpList(3,P).
P = [[1,2], [2,1], [1,1,1]].       % expected result

我很难弄清楚这一点,我开始认为这是不可能的。有任何想法吗?提前致谢。

【问题讨论】:

  • 不是不可能的,但你不会想为非常大的整数运行这个东西!阅读stackoverflow.com/questions/4632322/…可能会获得一些见解。
  • 嗯,我明白了......但这些答案取决于将一组数字作为输入传递给他们。就我而言,我需要生成一组数字......我不知道我是否清楚。
  • 不,你很清楚;另一个问题解决了给定加数的子集和问题,而您想要所有组合。它相似但不一样。您的问题将生成非常庞大的列表。只是出于好奇,这是作业吗?你想对结果列表的元素做什么。您知道给定总和的结果列表有多大吗?
  • 我知道它可以生成巨大的列表,但我只需要在 [1 ... 10] 之间加起来的加数(使最大可能的列表 [1,1,1,1,1, 1,1,1,1,1])。整个列表将传递给另一个谓词,以构建 R-ary 树。坦率地说,是的,这是家庭作业,我已经被困在试图弄清楚至少一个星期了。
  • 只是想评论一下,这与枚举所有可能的整数分区有关,有一些已知的方法可以做到这一点。包含相关标签会很有帮助。

标签: prolog


【解决方案1】:

试试这个:

condense([], Rs, Rs).
condense([X|Xs], Ys, Zs) :-
    condense(Xs, [X|Ys], Zs).
condense([X, Y|Xs], Ys, Zs) :-
    Z is X + Y,
    condense([Z|Xs], Ys, Zs).

condense(Xs, Rs) :-
    condense(Xs, [], Rs).

expand(0, []).
expand(N, [1|Ns]) :-
    N > 0,
    N1 is N - 1,
    expand(N1, Ns).

addUpList(N, Zs) :-
    expand(N, Xs),
    findall(Ys, condense(Xs, Ys), Zs).

让我知道我得到了什么分数。 :-)

【讨论】:

  • 天哪! Thats it! I think that's just what I need, Im 得仔细看看。非常感谢!
【解决方案2】:

规则num_split/2 生成将数字拆分为列表的方法,其中第一个元素X1N 之间的任意数字,列表的其余部分是N-X 的拆分。

num_split(0, []).
num_split(N, [X | List]) :-
    between(1, N, X),
    plus(X, Y, N),
    num_split(Y, List).

要获得所有此类拆分,只需在num_split/2 上致电findall/3

add_up_list(N, Splits) :-
    findall(Split, num_split(N, Split), Splits).

使用示例:

?- add_up_list(4, Splits).
Splits =
   [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]].

另请参阅@hardmath 的帖子,它给出了相同的答案并提供了更多解释。

【讨论】:

    【解决方案3】:

    问题中给出的示例表明任何正整数 N ≤ 的 compositions (ordered partitions) 10个被通缉。但是请注意,N=3 的解决方案 [3] 似乎已被省略/忽略。 N 的number of compositions 是 2^(N-1),因此 N=10 给出了一个很长的列表,但不是一个难以管理的列表。

    还希望将所有此类解决方案收集到一个列表中,这是findall/3 在我们编写一个生成它们的谓词composition/2 之后通常可以做的事情。

    这个想法是选择第一个和,介于 1 和 N 之间的任何值,从总数中减去它并递归(当总数达到零时以空列表停止)。 SWI-Prolog 提供了一个谓词between/3,它可以生成那些可能的第一个和,Amzi! Prolog 提供了一个类似的谓词for/4。为了便于移植,我们在这里编写自己的版本。

    summand(Low,High,_) :-
        Low > High,
        !,
        fail.
    summand(Low,High,Low).
    summand(Low,High,Val) :-
        Now is Low + 1,
        summand(Now,High,Val).
    
    composition(0,[ ]).
    composition(N,[H|T]) :-
        summand(1,N,H),
        M is N - H,
        composition(M,T).
    

    鉴于上述 Prolog 源代码,编译或解释,所有解决方案的列表可以通过这种方式获得:

    ?- findall(C,composition(3,C),L).
    
    C = H126
    L = [[1, 1, 1], [1, 2], [2, 1], [3]] 
    

    当然,您的特定应用程序可能需要对此类解决方案列表进行一些安排或省略单例列表,但这并不清楚,因为问题目前的措辞。

    【讨论】:

      【解决方案4】:

      这个问题已经有很多很好的答案,但这里有另一个解决方案供您考虑。该程序与其他程序的不同之处在于它非常高效,并生成列表的非冗余解,这些解被假定为表示集合的整数,这些整数加起来达到指定的数量.

      gen(N, L) :-
          gen(N-1, N, N, FL),
          dup_n(FL, L).
      gen(C-F, M, M, [C-F]).
      gen(C-F, S, M, [C-F|R]) :-
          S < M, C > 1,
          C0 is C - 1,
          F0 is floor(M / C0),
          S0 is S + (C0 * F0),
          gen(C0-F0, S0, M, R).
      gen(C-F, S, M, R) :-
          F > 0,
          F0 is F - 1,
          S0 is S - C,
          gen(C-F0, S0, M, R).
      
      dup_n([], []).
      dup_n([_-0|R], L) :-
          !, dup_n(R, L).
      dup_n([V-F|R], [V|L]) :-
          F0 is F - 1,
          dup_n([V-F0|R], L).
      

      您可以通过以下方式实现addUpList/2

      addUpList(N, P) :-
          findall(L, gen(N, L), P).
      

      这应该会给你以下行为:

      ?- addUpList(4,L).
      L = [[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]].
      

      请注意,包含一个2 和两个1s 的列表在此结果集中只出现一次;这是因为gen/4 计算加起来为指定数字的唯一整数集。

      【讨论】:

        【解决方案5】:

        This answer 介于两者之间 @Kaarel's answer@sharky's "efficient" answer.

        就像@sharky 的代码一样,我们在相邻列表项之间施加了一种排序关系,以限制解决方案空间的大小——知道如何在需要时对其进行膨胀。所以@sharky 的break_down/2gen/2 的解集是相等的(忽略列表反转)。

        至于性能,请考虑:

        ?- time((break_down(40,_),false))。
        % 861,232 推理,0.066 CPU 在 0.066 秒内(100% CPU,13127147 唇)
        错误的。
        
        ?-时间((gen(40,_),false))。
        % 8,580,839 次推理,0.842 CPU 在 0.842 秒内(100% CPU,10185807 唇)
        错误的。
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-06
          • 1970-01-01
          • 1970-01-01
          • 2023-02-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多