列出1-50范围内的50个随机数,使相邻数字不在15之内

时间:2015-10-04 16:40:07

标签: python arrays list random

这是我到目前为止的情况,但如果我超过前一个数字的正负11就会中断。因此,如果前一个数字为1,则下一个数字必须大于16.另外,我只想使用每个数字一次。

示例输出:

[3,45, 1, 16, 33, 3, 23.....]

到目前为止我的脚本是:

import random
new_array=[]
counter = 0

array=range(51)
array=array[1:51]

while len(new_array)<50:
    y=random.choice(array)
    if y not in new_array and counter!=0 and y not in (range(new_array[counter-1]-11,new_array[counter-1]+11)):
        new_array.append(y)
        counter+=1
    elif counter == 0:
        new_array.append(y)
        counter+=1
    else:
        pass

7 个答案:

答案 0 :(得分:1)

以下方法如何?从洗牌列表开始。对于每对,如果距离小于15,则旋转剩余的数字。

import random

def get_shuffled_list(length):
    output = range(1, length+1)
    i = 0
    sanity = 0

    while i < length and sanity == 0:
        random.shuffle(output)
        for i in range(1, length):
            sanity = length - i + 1

            while abs(output[i-1] - output[i]) <= 15 and sanity:
                output.append(output[i])
                del output[i]
                sanity -= 1

            if sanity == 0:
                break

    return output

print get_shuffled_list(50)

提供以下类型的输出:

[1, 38, 3, 33, 16, 43, 10, 41, 9, 35, 4, 50, 29, 8, 44, 28, 5, 32, 14, 40, 21, 47, 25, 48, 30, 12, 34, 18, 42, 15, 36, 13, 31, 2, 24, 7, 23, 39, 22, 6, 26, 49, 27, 11, 45, 19, 46, 17, 37, 20]

如果您没有任何符合条件的剩余数字,则会在最后出现问题,在这种情况下,请重新开始。对于10,000次洗牌的测试,我管理的最糟糕的情况是196次尝试。不理想,但确实有效。

答案 1 :(得分:0)

尝试这样的事情:

  • last的初始值开头。 该值应为字符串('start')。
  • 循环多次你需要:
    • 检查是否last == 'start'
    • 如果是,请在列表中添加一个随机数。
    • 如果不是,请创建一个随机数。
      • abs(number - last)小于15之前,请创建一个新的随机数。
      • 将号码添加到列表中。
    • last设置为列表的最后一个元素。

答案 2 :(得分:0)

编辑:没有回答这个问题,因为它可以并且会多次选择一些数字。留下它以防这个变种对其他人有用。

您使用的算法和JF建议的算法共享一个有趣的缺陷:它们所用的时间没有限制。如果你真的不走运,那么在宇宙结束之前函数就无法返回。

这是一个将在约束时间内执行的,即O(n)

import random

def spaced_randoms(min_val, max_val, number, space):
    if max_val - min_val <= 2 * space:
        raise ValueError('interval not large enough for space')

    last = random.randint(min_val, max_val)
    result = [last]
    for _ in range(number - 1):
        skip_low = max(0, min(space, last - min_val))
        skip_high = max(0, min(space, max_val - last))
        value = random.randint(min_val + skip_low, max_val - skip_high - 1)

        last = value - skip_low if value < last else value + skip_high + 1
        result.append(last)

    return result

>>> print(spaced_randoms(0, 50, 50, 15))
[35, 10, 44, 26, 47, 22, 49, 29, 3, 28, 44, 17, 40, 3, 39, 7, 48, 22, 41, 5, 21, 4, 32, 9, 34, 3, 46, 3, 20, 38, 10, 49, 32, 16, 38, 5, 26, 45, 26, 49, 13, 44, 1, 30, 12, 30, 0, 46, 15, 42]

这个想法很简单:我们减少新数字的可能间隔以考虑禁用值。我们重新映射结果。例如,假设某个点last == 32有15个空格,那么:

  • 我们选择15到34之间的数字。
  • 如果选择的号码在[15到31],我们会重新映射到[0到16]
  • 如果选择的号码在[32到34],我们重新映射到[48到50]

答案 3 :(得分:0)

嗯,沿着这条线的东西,用伪代码

sampled = []

n = 50

x = 15

curr = n + x + 1

for k in range(1, 1000):

    # build exclusion range tuple, where you cannot sample,
    # say for calc_ex_range(50, 15, 27) it should return
    # (12, 42)
    exclude_range = calc_ex_range(n, x, curr)

    # build new sampling array, given the exclusion range
    # result should be [1,2,3,4,5,6,7,8,9,10,11,43,44,45,46,47,48,49,50]
    sampling_array = calc_sarray(n, exclude_range)

    # remove already sampled numbers from sampling array
    sampling_array = filter_sarray(sampling_array[:], sampled)
    if len(sampling_array) == 0:
        break

    # sample one item from the array
    curr = random.sample(sampling_array, 1)

    sampled.append(curr)

复杂性将是O(N ^ 2),我猜,但它总是完成

更新

为了采样一次,过滤掉采样数组

答案 4 :(得分:0)

在另一个未能满足&#34的答案后,每个项目必须恰好出现一次&#34;要求,这里有一些想法:

如果我将间距称为两个连续数字之间的最小绝对差值,则:

  • 选择数组的开头很容易。你可以用Severin Pappadeux的代码来做到这一点。
  • 留下2 * 间距未选择的项目后,您必须切换算法。
  • 选择最后一项的问题似乎是P-complete。

不幸的是,我对算法理论不够好,无法建立正式的证据。而且,也许它只是我找不到更好的方法。但在这一点上,我没有看到任何方法,只能将最后一项作为图搜索进行选择。

然而,有趣的是,我无法承受更多时间。但如果你回来回答你自己的问题,我会喜欢阅读它。

答案 5 :(得分:0)

如果可以随意排除符合您标准的许多排列,您可以非常轻松地创建一个算法来生成可行的排列。

我能想到的最简单的例子如下:

  • 将范围{1,50}划分为10个子范围,包含五个数字:{1,5},{6,10},{11,15}...
  • 选择这些子范围的序列,使得对于彼此相邻的任何两个子范围,最接近的元素至少相隔15个。最简单的方法是选择每个第四个子范围:{1,5},{21,25},{41,45},{11,15}...
  • 输出序列以每个范围中随机选择的一个数字开头:3,22,41,15...
  • 继续从子范围中选择数字,直到您从每个子范围中选取所有5个数字(即直到您选择了从0到50的整个范围内的每个数字):3,22,41,15...1,24,42,14...4,21...

您可以通过选择不同的子范围大小(例如157来对此进行简单的变体,尽管这会引入额外的复杂性,即不能均匀地划分原始范围)。或者,您可以按照永远不会使序列无效的规则随机转置输出序列的元素(或以其他方式改变序列)(即,您不能引入小于15的元素配对)。您还可以使子范围的排序为准随机,或者通过子范围更改ach迭代的排序。

即使有这些变化,这显然也不是任何可能的有效排列的完全随机选择。但在实践中,我预计这不太重要。

答案 6 :(得分:-1)

这个怎么样?我使用randint轻松获得所需范围内的整数:

import random

my_list = []
# This number should not be within 15 units from each
# new random integer:
previous = random.randint(1, 50)

# Loop until my_list' length is 50; see at the bottom
while True:
    # Get a new random integer from 1 to 50
    rand_int = random.randint(1, 50)

    # If the new integer is NOT at a range within 15 of the previous number,
    # append to the list; otherwise skip and try again
    if not rand_int - 15 < previous < rand_int + 15:
        my_list.append(rand_int)
        # This new integer becomes the new previous
        previous = rand_int

    if len(my_list) >= 50:
        # Break from the loop and print the new list
        break

print(my_list)