以下实现将永久循环。如何从列表中随机选择元素?
pick_nums(0,_,_).
pick_nums(Count,From,To) :-
random_member(X,From),
(member(X,To) -> pick_nums(Count,From,To)
; C1 is Count-1,
pick_nums(C1,From,[X|To])) .
?- numlist(1,9,X), pick_nums(3,X,Y).
以下实现将永久循环。如何从列表中随机选择元素?
pick_nums(0,_,_).
pick_nums(Count,From,To) :-
random_member(X,From),
(member(X,To) -> pick_nums(Count,From,To)
; C1 is Count-1,
pick_nums(C1,From,[X|To])) .
?- numlist(1,9,X), pick_nums(3,X,Y).
这里的问题是,里面pick_nums(Count, From, To)
你有pick_nums(Count, From, To)
相同的电话。这里的条件逻辑似乎在说:“给我一个From的随机元素,但如果我已经在To中有它,请再试一次;否则,正常重现。”这里的基本问题只是你的病情总是成功。
我确信有一个更直接的解决方案来解决您的问题,而不是从头开始重写,但它对我来说似乎有点单一的Prolog。相反,让我们重新考虑我们想要说的话,以便我们可以更直接地说“逻辑”。你实际上想说的是“给我一个随机的子集,从一定的尺寸。”原始代码中的条件实际上仅仅是“嘿,我已经看到了这个元素,所以让我们继续前进”的陪衬,但是在Prolog中经常会说出你的意思,而不是告诉Prolog如何去做。
pick_nums(0, _, []).
pick_nums(Count, From, [X|SelectedFromRemaining]) :-
random_member(X, From),
select(X, From, Remaining),
C1 is Count - 1,
pick_nums(C1, Remaining, SelectedFromRemaining).
通过使用select/3
我能够产生列表而不在其选定的元素,然后我就可以传递给下一个电话。这样我就不用担心我是否已经看过这个元素。请注意,这样做可能会导致性能损失,但是我们用更少的语句达到了这一目标,并且(希望)更清楚我们要做什么。
如果我们愿意进一步依赖SWI-Prolog,有一些内置库可以进一步简化这个过程。例如,random
库有一个谓词,它会给我们一个列表的随机排列。我们可以说明我们清楚在做什么漂亮的使用:
pick_nums(Count, From, To) :-
random_permutation(From, Scrambled),
append(To, _, Scrambled),
length(To, Count).
如果你总是期待与numlist
用它那里面有另一个帮手已经做了这个:
?- randset(3, 9, X).
X = [3, 4, 8].
?- randset(3, 9, X).
X = [2, 7, 9].
我希望这是足够的帮助。
我觉得问题是电话member(X,To)
。 var To
尚未实例化,如果首先从数字中选择5(使用调试器查看这些详细信息!),它将变为To = [5|_G397]
。
纠正你的代码,我看到的却是加入累加器的最简单方法:
pick_nums(Count, From, To) :-
pick_nums(Count, From, [], To).
pick_nums(0, _, Acc, Acc).
pick_nums(Count, From, Acc, To) :-
Count > 0, % avoid looping on backtracking
random_member(X, From),
( memberchk(X, Acc)
-> pick_nums(Count, From, Acc, To)
; C1 is Count-1,
pick_nums(C1, From, [X|Acc], To)
) .
我还添加了一个“警卫”,以避免循环的回溯
完美的答案,谢谢! –