2013-01-16 135 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) 
    ) . 

我還添加了一個「警衛」,以避免循環的回溯