简体   繁体   English

Python - 从列表中提取随机数。 使用指定的长度和总和填充新列表

[英]Python - Pull random numbers from a list. Populate a new list with a specified length and sum

I am trying to create a function where:我正在尝试创建一个函数,其中:

  1. The output list is generated from random numbers from the input list输出列表由输入列表中的随机数生成
  2. The output list is a specified length and adds to a specified sum输出列表是指定长度并添加到指定总和

ex.前任。 I specify that I want a list that is 4 in length and adds up to 10. random numbers are pulled from the input list until the criteria is satisfied.我指定我想要一个长度为 4 并且加起来为 10 的列表。从输入列表中提取随机数,直到满足条件。

I feel like I am approaching this problem all wrong trying to use recursion.我觉得我尝试使用递归来解决这个问题都是错误的。 Any help will be greatly appreciated!!!任何帮助将不胜感激!!!

EDIT: for more context on this problem.... Its going to be a random enemy generator.编辑:关于这个问题的更多背景......它将是一个随机的敌人生成器。

The end goal input list will be coming from a column in a CSV called XP.最终目标输入列表将来自一个名为 XP 的 CSV 列。 (I plan to use pandas module). (我打算使用熊猫模块)。 But this CSV will have a list of enemy names in the one column, XP in another, Health in another, etc. So the end goal is to be able to specify the total number of enemies and what the sum XP should be between those enemies and have the list generate with the appropriate information.但是这个 CSV 将在一列中列出敌人的名字,在另一列中列出 XP,在另一列中列出生命值,等等。所以最终目标是能够指定敌人的总数以及这些敌人之间的 XP 总和应该是多少并使用适当的信息生成列表。 For ex.例如。 5 enemies with a total of 200 XP between them. 5 个敌人,总共 200 XP。 The result is maybe -> Apprentice Wizard(50 xp), Apprentice Wizard(50 xp), Grung(50), Xvart(25 xp), Xvart(25 xp).结果可能是 -> Apprentice Wizard(50 xp)、Apprentice Wizard(50 xp)、Grung(50)、Xvart(25 xp)、Xvart(25 xp)。 The output list will actually need to include all of the row information for the selected items.输出列表实际上需要包含所选项目的所有行信息。 And it is totally fine to have duplicated in the output as seen in this example.如本例所示,在输出中进行复制是完全没问题的。 That will actually make more sense in the narrative of the game that this is for.这实际上在游戏的叙述中更有意义。

The csv --> https://docs.google.com/spreadsheets/d/1PjnN00bikJfY7mO3xt4nV5Ua1yOIsh8DycGqed6hWD8/edit?usp=sharing 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)

I guess that it would be better to get first all n-size combinations of given array which adds to specified number, and then randomly select one of them.我想最好先获得给定数组的所有 n 大小组合,这些组合添加到指定的数字,然后随机选择其中一个。 Random selecting and checking if sum is equal to specified value, in pessimistic scenario, can last indefinitely.随机选择并检查 sum 是否等于指定值,在悲观场景中,可以无限期持续。

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


If the numbers in your input list are consecutive numbers, then this is equivalent to the problem of choosing a uniform random output list of N integers in the range [min, max], where the output list is ordered randomly and min and max are the smallest and largest number in the input list.如果你的输入列表中的数字是连续的数字,那么这相当于选择一个由 [min, max] 范围内的 N 个整数组成的统一随机输出列表的问题,其中输出列表是随机排序的,min 和 max 是输入列表中的最小和最大数字。 The Python code below shows how this can be solved.下面的 Python 代码展示了如何解决这个问题。 It has the following advantages:它具有以下优点:

  • It does not use rejection sampling.它不使用拒绝抽样。
  • It chooses uniformly at random from among all combinations that meet the requirements.它从满足要求的所有组合中随机均匀地选择。

It's based on an algorithm by John McClane, which he posted as an answer to another question .它基于 John McClane 的算法,他将其发布为另一个问题答案 I describe the algorithm in another answer.我在另一个答案中描述了该算法

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

Example:例子:

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]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM