【问题标题】:Python - Pull random numbers from a list. Populate a new list with a specified length and sumPython - 从列表中提取随机数。填充具有指定长度和总和的新列表
【发布时间】:2020-06-25 23:29:27
【问题描述】:

我正在尝试创建一个函数,其中:

  1. 输出列表由输入列表中的随机数生成
  2. 输出列表是指定长度并添加到指定总和

例如。我指定我想要一个长度为 4 且加起来为 10 的列表。从输入列表中提取随机数,直到满足条件。

我觉得我试图使用递归来解决这个问题都是错误的。任何帮助将不胜感激!!!

编辑:有关此问题的更多上下文....它将是一个随机敌人生成器。

最终目标输入列表将来自 CSV 中名为 XP 的列。 (我打算使用熊猫模块)。但是这个 CSV 将在一个列中列出敌人名称,在另一列中有 XP,在另一列中有 Health,等等。所以最终目标是能够指定敌人的总数以及这些敌人之间的 XP 总和应该是多少并使用适当的信息生成列表。例如。 5 个敌人,他们之间总共有 200 XP。结果可能是 -> Apprentice Wizard(50 xp)、Apprentice Wizard(50 xp)、Grung(50)、Xvart(25 xp)、Xvart(25 xp)。输出列表实际上需要包含所选项目的所有行信息。如本例所示,在输出中复制完全没问题。这实际上在游戏的叙述中更有意义。

csv --> https://docs.google.com/spreadsheets/d/1PjnN00bikJfY7mO3xt4nV5Ua1yOIsh8DycGqed6hWD8/edit?usp=sharing

import random
from random import *

lis = [1,2,3,4,5,6,7,8,9,10]

output = []

def query (total, numReturns, myList, counter):

    random_index = randrange(len(myList)-1)
    i = myList[random_index]
    h = myList[i]

    # if the problem hasn't been solved yet...
    if len(output) != numReturns and sum(output) != total:
        print(output)

        # if the length of the list is 0 (if we just started), then go ahead and add h to the output
        if len(output) == 0 and sum(output) + h != total:
            output.append(h)
            query (total, numReturns, myList, counter)


        #if the length of the output is greater than 0
        if len(output) > 0:

            # if the length plus 1 is less than or equal to the number numReturns
            if len(output) +1 <= numReturns:
                print(output)

                #if the sum of list plus h is greater than the total..then h is too big. We need to try another number
                if sum(output) + h > total:
                    # start counter

                    for i in myList:#  try all numbers in myList...
                        print(output)
                        print ("counter is ", counter, " and i is", i)
                        counter += 1
                        print(counter)

                        if sum(output) + i == total:
                            output.append(i)
                            counter = 0
                            break
                        if sum(output) + i != total:
                           pass
                        if counter == len(myList):
                            del(output[-1]) #delete last item in list
                            print(output)
                            counter = 0 # reset the counter
                    else:
                        pass

                #if the sum of list plus h is less than the total
                if sum(output) + h < total:
                    output.append(h) # add h to the list

        print(output)
        query (total, numReturns, myList, counter)


    if len(output) == numReturns and sum(output) == total:
        print(output, 'It worked')
    else:
        print ("it did not work")


query(10, 4, lis, 0)

【问题讨论】:

  • 数字可以重复吗?例如[2, 2, 3, 3]query(10, 4, lis, 0) 的有效解决方案吗?
  • 嗨尼克,是的,数字可以重复。 [2, 2, 3, 3] 是我正在寻找的完美解决方案。谢谢!
  • 您接受的答案不会生成[2, 2, 3, 3] 作为解决方案。
  • 感谢您发现尼克,我没有意识到。在这种情况下,实际上最好使用重复的数字。
  • 这似乎是背包问题的化身

标签: python list recursion random sum


【解决方案1】:

如果你的输入列表中的数字是连续的数字,那么这相当于选择一个范围为 [min, max] 的 N 个整数的均匀随机输出列表的问题,其中输出列表是随机排序的,min 和max 是输入列表中的最小和最大数字。下面的 Python 代码显示了如何解决这个问题。它具有以下优点:

  • 它不使用拒绝抽样。
  • 从所有符合要求的组合中均匀随机选择。

它基于 John McClane 的算法,他将其发布为 answer to another question。我在另一个答案中describe the algorithm

import random # Or secrets

def _getSolTable(n, mn, mx, sum):
        t = [[0 for i in range(sum + 1)] for j in range(n + 1)]
        t[0][0] = 1
        for i in range(1, n + 1):
            for j in range(0, sum + 1):
                jm = max(j - (mx - mn), 0)
                v = 0
                for k in range(jm, j + 1):
                    v += t[i - 1][k]
                t[i][j] = v
        return t

def intsInRangeWithSum(numSamples, numPerSample, mn, mx, sum):
        """ Generates one or more combinations of
           'numPerSample' numbers each, where each
           combination's numbers sum to 'sum' and are listed
           in any order, and each
           number is in the interval '[mn, mx]'.
            The combinations are chosen uniformly at random.
               'mn', 'mx', and
           'sum' may not be negative.  Returns an empty
           list if 'numSamples' is zero.
            The algorithm is thanks to a _Stack Overflow_
          answer (`questions/61393463`) by John McClane.
          Raises an error if there is no solution for the given
          parameters.  """
        adjsum = sum - numPerSample * mn
        # Min, max, sum negative
        if mn < 0 or mx < 0 or sum < 0:
            raise ValueError
        # No solution
        if numPerSample * mx < sum:
            raise ValueError
        if numPerSample * mn > sum:
            raise ValueError
        if numSamples == 0:
            return []
        # One solution
        if numPerSample * mx == sum:
            return [[mx for i in range(numPerSample)] for i in range(numSamples)]
        if numPerSample * mn == sum:
            return [[mn for i in range(numPerSample)] for i in range(numSamples)]
        samples = [None for i in range(numSamples)]
        table = _getSolTable(numPerSample, mn, mx, adjsum)
        for sample in range(numSamples):
            s = adjsum
            ret = [0 for i in range(numPerSample)]
            for ib in range(numPerSample):
                i = numPerSample - 1 - ib
                # Or secrets.randbelow(table[i + 1][s])
                v = random.randint(0, table[i + 1][s] - 1)
                r = mn
                v -= table[i][s]
                while v >= 0:
                    s -= 1
                    r += 1
                    v -= table[i][s]
                ret[i] = r
            samples[sample] = ret
        return samples

例子:

weights=intsInRangeWithSum(
   # One sample
   1,
   # Count of numbers per sample
   4,
   # Range of the random numbers
   1, 5,
   # Sum of the numbers
   10)
# Divide by 100 to get weights that sum to 1
weights=[x/20.0 for x in weights[0]]

【讨论】:

    【解决方案2】:

    我想最好先获取给定数组的所有 n 大小组合,这些组合添加到指定的数字,然后随机选择其中一个。随机选择和检查总和是否等于指定值,在悲观的情况下,可以无限期地持续下去。

    from itertools import combinations as comb
    from random import randint
    
    x = [1,1,2,4,3,1,5,2,6]
    
    def query(arr, total, size):
      combs = [c for c in list(comb(arr, size)) if sum(c)==total]
      return combs[randint(0, len(combs))]
    
    #example 4-item array with items from x, which adds to 10
    print(query(x, 10, 4))
    
    
    

    【讨论】:

    • 您实际上并不想生成组合。您只需要知道数字 (N),并有一种确定的方式将 [0, N) 中的数字映射到组合。那么你需要做的就是生成一个随机数。
    • @MadPhysicist 你有没有机会把它写下来作为答案?我很好奇它是如何工作的。
    • 这不会生成[2,2,3,3] 作为解决方案。请参阅问题的 cmets。
    • 我可能必须解决背包问题才能做到这一点...... :)
    • 你也可以用计数器构建生成器来生成n个大小的组合,并放入指示何时返回的随机数,以免生成所有组合。如果您希望重复数字,例如 [1,2,3,...,10] 中的 [2,2,3,3],那么您可以使用 numpy repeat,将列表中的所有数字重复 n 次,例如想要大小为4的列表,输入列表修改为[1,1,1,1,2,2,2,2,....,10,10,10,10],然后提取组合。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-05
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多