概率分布与浮点变量,概率必须加到1

2 投票
1 回答
1510 浏览
提问于 2025-04-18 18:41

我正在写一个脚本,流程是这样的:程序会分析一堆用特定语言写的文本文件,然后为每个字母后面出现的第一个字符绘制概率分布。这里的k就是指这个字符。接着,程序利用这些信息尝试用马尔可夫链生成“真实”的单词。

我已经写好了大部分代码,程序也能生成一些有趣的单词。关键是,生成单词的函数使用了“尝试和捕获”的机制,以避免程序卡住。程序会卡住是因为有些概率分布的总和并不等于1(我猜是因为浮点数的精度问题或者其他原因),而numpy函数在处理这些分布时会因为概率不等于1而抛出一个错误。

由于某些分布触发了异常,导致有些单词根本无法生成,最终的结果就没有那么有趣了。

现在,我想问的是:有没有办法让这些概率分布在生成时就能加起来等于1?我试过gmpy2和round()函数,但似乎都不管用。也许这个问题有点傻,我只是需要透透气……无论如何,任何帮助都很有用!

这是生成概率分布的代码:

def FreqRel(self,listValues):
    absFreq = self.AbsFreq(listValues)
    freqRel = []
    for i in absFreq:
        freqRel.append(i/sum(absFreq))
    if sum(freqRel) != 1:
        print("Frequencies do not add up to 1")
        if sum(freqRel) - 1 < 0:
            diff = sum(freqRel) - 1
            #This should be an adjustment which should not interfere
            #that much on the probability distribution
            freqRel[1] = freqRel[1] - diff
            print("missing",diff)
        elif sum(freqRel) - 1 > 0:
            diff = sum(freqRel) - 1
            #This should be an adjustment which should not interfere
            #that much on the probability distribution
            freqRel[1] = freqRel[1] - diff
            print("Too much",diff)
    return freqRel

这是我在运行这个函数时在控制台上看到的输出:

enter image description here

enter image description here

这是在总和不等于1时崩溃的代码。崩溃的地方是numpy的那几行,错误信息是:ValueError: probabilities do not add up to 1.

def spitText(n):
    i = 0
    while i < n:
        try:
            word = ""
            #This oldChar setting is arbitrary, later I'm going to fix it
            oldChar = "b"
            for k in range(np.random.choice(distributions[0],replace=True,p=distributions[1])):
                newChar = np.random.choice(alphabet,replace=True,p=distRel[alphabet.index(oldChar)])
                word = word + newChar
                oldChar = newChar
            print(word)
            time.sleep(0.2)
            i+=1
        except:
            pass

1 个回答

4

你有一些输出,看起来像这样:

1.0
1.0
1.0
0
1.0
1.0

根据一个评论:

这是一个简单的循环,位于这个函数外面,它打印出这个函数返回的每个分布的总和。

所以,你的一些频率分布的总和是 0。这就是你的问题所在。

可以推测,你用来构建分布的代码中有一些特殊情况,导致返回了一个空的分布,或者返回的分布全是零。无论是哪种情况,显然都是不行的。


很多 1.0 的值因为累积的舍入误差可能达到 8e-17,这个问题其实并不重要。你可以看到,numpy 是为了处理这些情况而设计的:

>>> np.random.choice(2, 3, p=[0.4, 0.6+3e-17])
array([1, 0, 0])

只有当误差变得足够大时(大多数 numpy 的默认相对误差是 1e-5),它才会发出警告:

>>> np.random.choice(2, 3, p=[0.4, 0.6+3e-5])
ValueError: probabilities do not sum to 1

所以,你一定有一些概率分布,它们的总和与 1 的差距超过了 1e-5。当然,你确实有一些分布的总和差了整整 1


这就引出了你的主要问题:

有没有办法让这些概率分布在生成时加起来等于 1?

… 其实这是一个 XY问题:这并不是你需要解决的关键问题。

不过我还是会回答这个问题。简短的回答是:不可以。浮点数是固定精度的二进制分数。如果你试图用浮点数存储任意的实数,就会出现舍入误差。你可以很容易地看到这一点:

>>> 1.0 + 1e-17
1.0

实际上没有足够的位数来将 1.01.00000000000000001 作为不同的二进制分数存储。

如果你想进一步了解(而且你应该了解),可以阅读 每个计算机科学家都应该知道的浮点数知识,这是关于这个主题的经典入门文章。

撰写回答