random.sample 在函数内迭代时产生相同的输出

huangapple go评论68阅读模式
英文:

random.sample producing same output when iterated over inside function

问题

我正在尝试编写一个带有蒙特卡洛模拟的扑克手牌评估器。

在模拟游戏结果(例如发牌)时,在使用random.sample进行随机化的函数内部,随机化正常运作。

def simulate(hands, board):
    # 创建牌堆
    suits = ['d','s','c','h']
    ranks = ['A','2','3','4','5','6','7','8','9','T','J','Q','K']
    cards = []
    for r in ranks:
        for s in suits:
            cards.append(r+s)
    
    # 洗牌
    deck = random.sample(cards, len(cards)) 
    # 从牌堆中移除公共牌和玩家手牌
    deck = list(filter(lambda x: x not in board, deck))
    for hand in hands:
          deck = list(filter(lambda x: x not in hand, deck))
    
    # 发转牌和河牌
    while len(board) < 5:
        card = deck.pop(0)
        board.append(card)
    return board
for i in range(2):
    outcome = simulate([['Ah', 'Ac'], ['7s', '6s']], ['2d', '5s', '8s'])
    print(outcome)
输出:
['2d', '5s', '8s', '9s', 'Jc']
['2d', '5s', '8s', '4s', '3s']

如果我将这段代码放入另一个函数中,随机化会失效,我一直得到相同的结果。

def monte_carlo(hands, board, samples=5):
    for i in range(samples):
        outcome = simulate(hands, board)
        print(outcome)

monte_carlo([['Ah', 'Ac'], ['7s', '6s']], ['2d', '5s', '8s'])

输出:
Board  ['2d', '5s', '8s', '2c', '4d']
None
Board  ['2d', '5s', '8s', '2c', '4d']
None
Board  ['2d', '5s', '8s', '2c', '4d']
None
Board  ['2d', '5s', '8s', '2c', '4d']
None
Board  ['2d', '5s', '8s', '2c', '4d']

这种行为的原因是什么?

英文:

I'm trying to write a poker hand evaluator with a monte carlo simulation.

When simulating game outcomes (e.g. dealing cards) inside a function using random.sample randomization works fine.

def simulate(hands, board):
    # create deck
    suits = [&#39;d&#39;,&#39;s&#39;,&#39;c&#39;,&#39;h&#39;]
    ranks = [&#39;A&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;,&#39;8&#39;,&#39;9&#39;,&#39;T&#39;,&#39;J&#39;,&#39;Q&#39;,&#39;K&#39;]
    cards = []
    for r in ranks:
        for s in suits:
            cards.append(r+s)
    
    # shuffle deck
    deck = random.sample(cards,len(cards)) 
    # remove board and player cards from deck
    deck = list(filter(lambda x: x not in board, deck))
    for hand in hands:
          deck = list(filter(lambda x: x not in hand, deck))
    
    # deal turn and river
    while len(board) &lt; 5:
        card = deck.pop(0)
        board.append(card)
    return board
for i in range(2):
    outcome = simulate([[&#39;Ah&#39;, &#39;Ac&#39;], [&#39;7s&#39;, &#39;6s&#39;]], [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;])
    print(outcome)
Output:
[&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;9s&#39;, &#39;Jc&#39;]
[&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;4s&#39;, &#39;3s&#39;]

If I run this inside a for loop it works fine but
once I put this into another function randomization fails and I keep getting the same result.

def monte_carlo(hands, board, samples=5):
    for i in range(samples):
        outcome = simulate(hands, board)
        print(outcome)

monte_carlo([[&#39;Ah&#39;, &#39;Ac&#39;], [&#39;7s&#39;, &#39;6s&#39;]], [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;])

Output:
Board  [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;2c&#39;, &#39;4d&#39;]
None
Board  [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;2c&#39;, &#39;4d&#39;]
None
Board  [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;2c&#39;, &#39;4d&#39;]
None
Board  [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;2c&#39;, &#39;4d&#39;]
None
Board  [&#39;2d&#39;, &#39;5s&#39;, &#39;8s&#39;, &#39;2c&#39;, &#39;4d&#39;]

What is the reason for this behaviour?

答案1

得分: 1

这是关于list类型以及它是可变的一个棘手的问题。如果你将这行代码更改为:outcome = simulate(hands, [b for b in board]),你将获得所期望的结果。

[b for b in board]只是创建一个新的列表,其中包含与board相同的元素。重要的是,在Python中,它在幕后实际上是一个不同的列表。它与新的内存相关联。如果不这样做,每次循环迭代中使用的board都是对完全相同列表的引用。为了可视化它,将这个调试添加到代码中:

def monte_carlo(hands, board, samples=5):
    print("现在我的板有值:", board)
    for i in range(samples):
        outcome = simulate(hands, board)
        print(outcome)

你会看到这个调试在时间上显示了board的值变化,尽管被打印出来的board位于simulate函数之外。因为列表是可变的,所有对列表的引用都指向完全相同的内存。这不是每次运行simulate(hands, board)时都会发送一个全新版本的board,就像你在调用monte_carlo(hands, board)时一样。不,你实际上是在发送相同的列表。并且因为在simulate内部你改变了列表(即添加元素),这些更改也存在于monte_carlo方法对board的引用中。

英文:

This is a tricky little piece around the list type and the fact it's mutable. If you change this line of code: outcome = simulate(hands, board) to outcome = simulate(hands, [b for b in board]) you'll get the desired result.

The [b for b in board] just creates a new list which has the same elements as board. Importantly, it is a different list behind the scenes in python. New memory is associated with it. If you don't do this, the board that is being used in each iteration of the loop is a reference to the exact same list. To visualise it, add this debug into the code:

def monte_carlo(hands, board, samples=5):
    print(&quot;NOW MY BOARD HAS VALUE: &quot;, board)
    for i in range(samples):
        outcome = simulate(hands, board)
        print(outcome)

You'll see that that debug shows the value of board changing in time, even though the board that's being printed is outside the simulate function. Because lists are mutable, all references to the list point to the exact same memory. It's not like each time you run simulate(hands, board) you're sending in a brand new version of board, exactly as it was when you called monte_carlo(hands, board). No, you're sending in the exact same list. And because inside simulate you're changing the list (i.e. adding elements), those changes also exist in the monte_carlo method's reference to board.

huangapple
  • 本文由 发表于 2023年6月27日 18:47:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76564088.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定