【问题标题】:How to generate 2 non-adjacent random numbers in a range如何在一个范围内生成 2 个不相邻的随机数
【发布时间】:2017-01-31 16:54:35
【问题描述】:

我需要在 [A..B] 范围内生成 2 个随机数,并限制数字不能相邻。 我想在恒定时间内完成(我不想一直画到第二个值好)。

我可以想到几种方法来做到这一点:

  • 选择第一个,然后选择适合它的后一个:从[A..B-2] 范围内绘制V1,然后从[V1+2..B] 范围内绘制V2
  • 选择它们之间的距离,然后放置它们:从[2..B-A] 绘制d,从[0..B-A-d] 绘制V1,然后V2=V1+d
  • 选择第一个,然后选择第二个的偏移量:从整个范围绘制 V1,然后从[A+2-V1..B-V1-1] 范围绘制 d,并设置 V2= d<=0 ? V1-2+d : V1+1+d
  • 选择第一个,然后用环绕选择到第二个的距离:从[A..B]选择V1,从[0..A-B-2]选择d,V2 = V1+dV2 = V2>B ? V2-(B-A)

我想要最随机的方法(产生最多的熵,分布最均匀)。我认为最后两个是等效的,比前两个更随机。还有更好的方法吗?

【问题讨论】:

  • 顺便说一句,重绘没有错 - 这就是当您的范围不是 2 的幂时确保均匀分布的方法。您的随机数函数可能已经在您不知情的情况下执行此操作。
  • 您建议的第四种方法似乎是最自然和最容易证明其性质的方法。
  • 是否允许V1 == V2
  • A 是否与B 相邻?是“循环”范围吗?
  • 为了便于讨论,A 和 B 至少相距 4 个单位。范围不换行。 V1 不能等于 V2。

标签: algorithm random


【解决方案1】:

假设范围是[0, n)。对于随机无序不相邻对,从[0, n-2) 生成一个随机无序对并将更大的元素增加2 就足够了。后者可以通过来自[0, (n+1)n/2) 的双射映射来完成。

import random


def randnonadjpair(n):
    i, j = randunordpair(n-2)
    return i, j+2


def randunordpair(n):
    i = random.randrange((n+1)*n//2)
    if n%2 == 1:
        if i < n:
            return i, n-1
        i -= n
        n -= 1
    h = n//2
    q, r = divmod(i, h)
    if q < h:
        return q, h + r
    q -= h
    if q <= r:
        return q, r
    return n-q, n-1-r

(此答案适用于有序对。)

2 (n-2) + (n-2) (n-3) = n^2 - 3 n + 2 的方法可以从n 的长度范围内选择两个有序的不相邻元素。在0n^2 - 3 n + 2 之间生成一个随机数x,然后将其双射映射到有效结果:

def biject(n, x):
    if x < n - 2:
        return (0, x + 2)
    x -= n - 2
    if x < n - 2:
        return (n - 1, x)
    x -= n - 2
    q, r = divmod(x, n - 3)
    return (q, r if r < q - 1 else r + 3)

【讨论】:

  • 我得到了你一半的可能性:对于 5 个元素,有 6 个有效组合,而不是 12 个,对吧?
  • @AShelly 如果你的意思是无序对,那么是的。
  • @AShelly 添加了无序版本。
【解决方案2】:

如果你想要最大熵,那么你的两个选择必须是独立的。因此,第二选秀权的价值不受第一选秀权的限制;两者都必须从可用的整个范围中选择。这意味着独立选择两个数字,将它们作为一对进行检查,如果这对不合适,则拒绝两者。在伪代码中,它看起来像:

function pickPair()
  repeat
    num1 <- random(A, B)
    num2 <- random(A, B)
  until (notAdjacent(num1, num2))
  return (num1, num2)
end function

您在方法notAdjacent() 中检查两个数字的约束。

您没有说明范围 [A..B] 的大小。给定一个相当大的范围,那么不得不拒绝一对的机会就很低。或者,始终选择固定数量的对并返回任何符合您标准的对:

function constantTimePickPair
  pairFound <- false
  repeats <- 5 // Or enough to ensure certainty of a valid pair.
  do repeats times
    num1 <- random(A, B)
    num2 <- random(A, B)
    if (notAdjacent(num1, num2))
      pairFound <- true
      result <- (num1, num2)
    end if
  end do

  if (NOT pairFound)
    throw error "Pair not found."
  end if

  return result
end function

您需要设置足够多的重复次数,以在统计上确定找到有效配对。

【讨论】:

  • 我正在研究自己的答案,但在考虑了你所说的之后,我相信这是唯一正确的方法。考虑案例 [1..5] 很有用;有 25 种不同的组合,但只有 12 种是有效的。选择任何单个组合的几率必须为 12 分之一,否则结果将不会均匀分布。如果您无限制地选择 V1,则每个值都有五分之一的机会,并且由于 5 不能平均分为 12,因此 不可能 在 V2 上得到均匀分布。
  • @MarkRansom 的第二次分布甚至不是因为选择极端元素的选择高于选择内部元素的选择。
  • @fl00r 在 [1..5] 的 12 个有效组合中,3 个是 (1,x),2 个是 (2,x)。如果 V1=1 或 V1=2 的几率相同,您分布不均。
【解决方案3】:

下面的方法怎么样:

V1 = rand(A..B)
V2 = rand(A+2..B-1)
V2 += V2 > V1 ? 1 : -2

另外,应该提到的是,对于第二个选择,您无法在此处获得均匀分布。

左侧和右侧的边框物品被拾取的机会略多。

内部数字的概率是(B-A-3)/(B-A),而边框元素的概率是(B-A-2)/(B-A)

【讨论】:

    【解决方案4】:

    这是我目前的计划:

    给定目标范围[A..B]。它的长度LA-B+1。我们想选择V1,V2,这样V2不在[V1-1..V1+1]范围内
    如果V1A,那么L-2 可能是V2
    如果V1A+1,则L-3 可能是V2
    ...
    扩展这个模式,我们得到P 的可能性总数为sum([1..L-2])(这是@David Eisenstat 提出的数字的一半)。

    如果我们在[0,P)范围内选择一个数字N,那么我们可以生成对应的组合:

    V1 = A
    T = L-2
    while (N >= T):
       N -= T
       T -= 1
       V1 += 1
    V2 = V2 + N + 2  
    

    【讨论】:

    • 你得到了一半的可能性,因为他认为 (1,3) 和 (3,1) 不同,但你认为它们是相同的。您的均匀性要求是否基于它们相同?
    • 我认为它们是一样的。
    【解决方案5】:

    我会这样做:

    • 从 [A..B] 中绘制 V1
    • 如果 V1 == A || V1 == B 从 [A..B-1] 绘制 V2,否则从 [A..B-2] 绘制 V2
    • 做:

      if(V2 >= V1 - 1) V2++;
      if(V2 >= V1 + 1) V2++;
      

    第一次检查确保V1 - 1不能是V2的值
    第二次检查确保V1 + 1 不能是V2 的值。
    或者,换句话说,这会将值重新映射到 [A..V1-2][V1][V1+2..B]。

    由于这不会丢弃或重复任何值,因此分布应该很好。

    此答案当前假定 V1 == V2 有效。


    其实不然,上面的分布会有偏差。
    如果N = B - A + 1

    • 对于一个数字 = A= B,有 N - 2 对包含它
    • 对于[A+1...B-1] 中的数字,只有N - 3 对包含它。

    计算M 的对数,在[1..M] 中画一个数字并将其映射回相应的对,如 David Eisenstats 的答案中所述。

    【讨论】:

    • 如果 (V1==A) 或 (V1==B) 只有一个相邻
    • 这个问题的表述并不精确,但我并不期望得到两个相同的值(不相邻)。
    • @JimD。这不是我的理解,我会问OP。如果这是真的,那就更简单了:从 [A..B-3] 绘制 V2 并执行 if(V2 &gt;= V1 - 1) V2 += 3(加上特殊情况)
    猜你喜欢
    • 2018-04-08
    • 1970-01-01
    • 2011-08-02
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多