2013-01-16 92 views
1

以下实现将永久循环。如何从列表中随机选择元素?

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). 

回答

2

这里的问题是,里面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]. 

我希望这是足够的帮助。

+0

完美的答案,谢谢! –

1

我觉得问题是电话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) 
    ) . 

我还添加了一个“警卫”,以避免循环的回溯