繁体   English   中英

如何在Python中生成随机数对,包括一个条目相同的对,并排除两个条目相同的对?

[英]How to generate random pairs of numbers in Python, including pairs with one entry being the same and excluding pairs with both entries being the same?

我正在使用Python,并为此使用了numpy。 我想生成随机数对。 我想排除两个条目具有相同编号的对的重复结果,我想包含仅一个条目具有相同编号的对的重复结果。

import numpy
numpy.random.choice(a,(m,n),replace=False) 

为此,但它完全排除了所有具有相同条目的教堂,即

import numpy
numpy.random.choice(a=2,(m=2,n=1),replace=False) 

仅给我(1,0)和(0,1),而不给我(1,1),(0,0),(1,0)和(0,1)。

我想要这样做是因为我想绘制一个随机的元组样本,该元组具有一个较大的a和一个较大的n(如上所用),而没有获得完全相同的tupel,则需要重复一次。 它也应该或多或少地有效。 有没有已经实现此目的的方法?

生成器随机唯一坐标:

from random import randint

def gencoordinates(m, n):
    seen = set()

    x, y = randint(m, n), randint(m, n)

    while True:
        seen.add((x, y))
        yield (x, y)
        x, y = randint(m, n), randint(m, n)
        while (x, y) in seen:
            x, y = randint(m, n), randint(m, n)

输出:

>>> g = gencoordinates(1, 100)
>>> next(g)
(42, 98)
>>> next(g)
(9, 5)
>>> next(g)
(89, 29)
>>> next(g)
(67, 56)
>>> next(g)
(63, 65)
>>> next(g)
(92, 66)
>>> next(g)
(11, 46)
>>> next(g)
(68, 21)
>>> next(g)
(85, 6)
>>> next(g)
(95, 97)
>>> next(g)
(20, 6)
>>> next(g)
(20, 86)

如您所见,巧合地重复了x坐标!

假设您的xy坐标都是0到n之间的整数。 对于小n,一个简单的方法可能是使用np.mgrid生成所有可能的xy坐标的np.mgrid ,将其np.mgrid(nx * ny, 2)数组,然后从中采样随机行:

nx, ny = 100, 200
xy = np.mgrid[:nx,:ny].reshape(2, -1).T
sample = xy.take(np.random.choice(xy.shape[0], 100, replace=False), axis=0)

如果nx和/或ny非常大,则创建所有可能坐标的数组可能会变得很昂贵,在这种情况下,最好使用生成器对象并跟踪先前使用的坐标,如James的回答。


按照@morningsun的建议,另一种方法是从一组nx * ny个索引中采样到展平的数组中,然后将它们直接转换为x,y坐标,从而避免构造可能的x,y排列的整个nx * ny数组。

为了进行比较,这是我针对N维数组推广的原始方法的版本,以及使用新方法的版本:

def sample_comb1(dims, nsamp):
    perm = np.indices(dims).reshape(len(dims), -1).T
    idx = np.random.choice(perm.shape[0], nsamp, replace=False)
    return perm.take(idx, axis=0)

def sample_comb2(dims, nsamp):
    idx = np.random.choice(np.prod(dims), nsamp, replace=False)
    return np.vstack(np.unravel_index(idx, dims)).T

在实践中并没有太大的区别,但是第二种方法的好处对于更大的数组变得更加明显:

In [1]: %timeit sample_comb1((100, 200), 100)
100 loops, best of 3: 2.59 ms per loop

In [2]: %timeit sample_comb2((100, 200), 100)
100 loops, best of 3: 2.4 ms per loop

In [3]: %timeit sample_comb1((1000, 2000), 100)
1 loops, best of 3: 341 ms per loop

In [4]: %timeit sample_comb2((1000, 2000), 100)
1 loops, best of 3: 319 ms per loop


如果您已安装scikit-learn,则sklearn.utils.random.sample_without_replacement提供了一种更快的方法来生成随机索引,而无需使用Floyd算法进行替换:

from sklearn.utils.random import sample_without_replacement

def sample_comb3(dims, nsamp):
    idx = sample_without_replacement(np.prod(dims), nsamp)
    return np.vstack(np.unravel_index(idx, dims)).T

In [5]: %timeit sample_comb3((1000, 2000), 100)
The slowest run took 4.49 times longer than the fastest. This could mean that an
intermediate result is being cached 
10000 loops, best of 3: 53.2 µs per loop

@James Miles的答案很好,但只是为了避免在无意中询问过多参数时出现无休止的循环,我建议执行以下操作(它还会删除某些重复项):

def gencoordinates(m, n):
    seen = set()
    x, y = randint(m, n), randint(m, n)
    while len(seen) < (n + 1 - m)**2:
        while (x, y) in seen:
            x, y = randint(m, n), randint(m, n)
        seen.add((x, y))
        yield (x, y)
    return

请注意,错误的值范围仍将向下传播。

暂无
暂无

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

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