面试题:如何让1-50的随机数概率是51-100的2倍

31 阅读7分钟

面试题:如何让1-50的随机数概率是51-100的2倍

问题描述

给定1-100的随机数范围,如何设计一个算法,使得生成1-50的概率是生成51-100的概率的2倍?

这个问题看似简单,但实际上考察了候选人对概率、随机数生成以及算法优化的理解。本文将介绍四种不同的实现方法,并分析它们的原理和优缺点。

分析思路

要实现不同区间的概率调整,核心思想是通过某种方式增加目标区间的被选中概率。常见的方法有以下几种:

  1. 扩展列表:直接增加低概率区间的元素数量
  2. 条件判断:根据随机条件选择不同的区间
  3. 取模运算:通过数学运算调整概率分布
  4. 加权选择:创建加权列表进行随机选择

解决方案

下面是四种不同的实现方法,每种方法都能达到相同的目标,但采用了不同的技术手段:

# 随机抽取 1-100,如何让 1-50 的概率是 51-100 的 2倍
import random


def test_method(f):
    """
    测试随机数生成方法的概率分布
    :param f: 随机数生成函数,返回1-100之间的整数
    """
    a = 0  # 统计1-50出现的次数
    b = 0  # 统计51-100出现的次数
    for i in range(100000):  # 进行100,000次测试
        x = f()
        if x <= 50:
            a += 1
        else:
            b += 1
    print(a, b, a / b)  # 输出统计结果和概率比


def method1():
    """
    方法1:通过扩展列表实现概率调整
    原理:创建一个包含1-100的列表,然后再添加一次1-50的数字,这样1-50每个数字出现的概率是51-100的2倍
    :return: 1-100之间的随机整数,其中1-50出现的概率是51-100的2倍
    """
    return random.choice([i for i in range(1, 101)] + ([i for i in range(1, 51)]))


def method2():
    """
    方法2:通过条件判断实现概率调整
    原理:以2/3的概率生成1-50的随机数,以1/3的概率生成51-100的随机数
    :return: 1-100之间的随机整数,其中1-50出现的概率是51-100的2倍
    """
    return (
        random.randint(1, 50) if random.randint(1, 3) <= 2 else random.randint(51, 100)
    )


def method3():
    """
    方法3:通过取模运算实现概率调整
    原理:生成0-149的随机数,对100取模后加1得到1-100
    这样0-49(对应1-50)会有两个区间映射(0-49和100-149),而50-99(对应51-100)只有一个区间映射
    :return: 1-100之间的随机整数,其中1-50出现的概率是51-100的2倍
    """
    return (random.randint(0, 149) % 100) + 1


def method4():
    """
    方法4:通过加权选择实现概率调整
    原理:创建一个包含三个元素的列表,其中两个是1-50的随机数,一个是51-100的随机数
    这样从列表中随机选择时,1-50出现的概率是51-100的2倍
    :return: 1-100之间的随机整数,其中1-50出现的概率是51-100的2倍
    """
    return random.choice(
        [
            random.randint(1, 50),
            random.randint(1, 50),
            random.randint(51, 100),
        ]
    )


test_method(method1)
test_method(method2)
test_method(method3)
test_method(method4)

方法详解

方法1:扩展列表法

def method1():
    return random.choice([i for i in range(1, 101)] + ([i for i in range(1, 51)]))

原理:这种方法通过创建一个包含重复元素的列表来调整概率。具体来说,它首先创建一个包含1-100所有数字的列表,然后再添加一个包含1-50所有数字的列表,这样列表的总长度为150,其中1-50每个数字出现2次,51-100每个数字出现1次。当使用random.choice()从这个列表中随机选择元素时,1-50出现的概率自然是51-100的2倍。

优点:实现简单直观,易于理解。 缺点:当数字范围较大时,会占用较多内存,效率较低。

方法2:条件判断法

def method2():
    return (
        random.randint(1, 50) if random.randint(1, 3) <= 2 else random.randint(51, 100)
    )

原理:这种方法通过条件判断来控制概率分布。首先生成一个1-3的随机数,如果结果是1或2(概率为2/3),则生成1-50的随机数;如果结果是3(概率为1/3),则生成51-100的随机数。这样,1-50出现的概率就是51-100的2倍。

优点:实现简单,内存占用小。 缺点:需要进行两次随机数生成,效率略低。

方法3:取模运算法

def method3():
    return (random.randint(0, 149) % 100) + 1

原理:这种方法通过数学运算来调整概率分布。它首先生成一个0-149的随机数(共150个数),然后对100取模得到0-99的结果,最后加1得到1-100的随机数。在0-149的范围内,0-49和100-149都会映射到0-49(对应1-50),而50-99映射到50-99(对应51-100)。因此,1-50出现的概率是51-100的2倍。

优点:实现高效,只需要一次随机数生成和简单的数学运算。 缺点:需要理解取模运算的原理,相对不够直观。

方法4:加权选择法

def method4():
    return random.choice(
        [
            random.randint(1, 50),
            random.randint(1, 50),
            random.randint(51, 100),
        ]
    )

原理:这种方法通过创建一个加权列表来调整概率。列表中包含三个元素,其中两个是1-50的随机数,一个是51-100的随机数。当使用random.choice()从这个列表中随机选择时,1-50出现的概率是51-100的2倍。

优点:实现简单,易于理解。 缺点:需要进行多次随机数生成(列表创建时3次,选择时1次),效率较低。

验证结果

运行上述代码,我们可以得到以下输出结果:

66856 33144 2.017137340091721
66731 33269 2.005801196308876
66805 33195 2.0125018828136767
66618 33382 1.9956263854772034

从输出结果可以看出,四种方法都成功地实现了1-50出现的概率约为51-100的2倍的目标(比例接近2.0)。

总结

通过这个技术面试题,我们学习了四种不同的概率调整方法:

  1. 扩展列表法:通过增加元素数量调整概率,简单直观但内存占用大。
  2. 条件判断法:通过随机条件控制概率分布,实现简单但需要多次随机数生成。
  3. 取模运算法:通过数学运算调整概率,高效但不够直观。
  4. 加权选择法:通过加权列表控制概率,简单直观但效率较低。

在实际应用中,我们可以根据具体需求选择合适的方法。如果对内存要求较高,可以选择条件判断法或取模运算法;如果更注重代码的可读性,可以选择扩展列表法或加权选择法。

这个问题不仅考察了对概率和随机数生成的理解,还考察了算法优化的能力,是一个很好的技术面试题。

灵感来源

森果云面试经历上面刚刚面试完森果云,2小时48分,接近 3 个小时,有点特别的一家公司,记录一下。 整个面试有三轮。 一 - 掘金