2

我有一段相当粗糙的代码,必须或多或少地随机生成一堆百分比,存储为十进制浮点数。也就是说,它决定材料一占总数的 13.307%,然后将其存储在 dict 中为 0.13307。

问题是,我永远无法让这些数字加起来正好是一个。老实说,我不完全确定问题是什么。这可能与花车的性质有关。

这是令人反感的代码,以其过于复杂的荣耀:

while not sum(atmosphere.values())>=1:
    #Choose a material randomly
    themat=random.choice(list(materials.values()))

    #If the randomly chosen material is gaseous at our predicted temperature...
    if themat.vapor < temp:
        #Choose a random percentage that it will make up of our planet's atmosphere, then put it in the atmos dict.
        atmosphere[themat]=round(random.uniform(0.001,0.5),5)

#Find out if the fractions add up to more than 1
difference=(sum(atmosphere.values())-1)
#If one does...
while difference > 0:
    #Choose a random constituent
    themat=random.choice(list(atmosphere.keys()))
    #If that constituent has a higher fraction value than the amount we'd need to reduce the total to 1...
    if atmosphere[themat]>(sum(atmosphere.values())-1):
        #Subtract that much from it.
        atmosphere[themat]-=difference
        #Then break the loop, since we're done and otherwise we'd go on removing bits of the atmosphere forever.
        break
    else:
        #Otherwise, halve its percentage and reduce difference by the amount we reduced the atmosphere 
        oldperc=atmosphere[themat]
        atmosphere[themat]=oldperc/2
        difference-=oldperc/2

#Then, finally, we correct any overcorrections the previous block made.
difference=(sum(atmosphere.values())-1)
if difference < 0:
    #Choose a random mat
    themat=random.choice(list(atmosphere.keys()))
    #Then add enough to it that the total is 1.
    atmosphere[themat]+=difference

对不起,如果我错过了一些明显的东西,或者没有提供重要的信息,但我现在很累,而且我已经尝试了好几天了。

4

5 回答 5

6

如果您的意思是找到两个加起来为 1.0 的值

我了解您想在 0.0 和 1.0 之间选择两个浮点数,使它们相加到 1.0。

做这个:

  • 选择两者中最大的 L。它必须在 0.5 和 1.0 之间。
  • 将最小数 S 定义为 1.0 - L。

那么在浮点数中,S + L 正好是 1.0。


如果由于某种原因,您首先在算法中获得了最小的数 S,则计算 L = 1.0 - S,然后计算 S0 = 1.0 - L。然后 L 和 S0 加起来正好为 1.0。考虑 S0 是 S 的“四舍五入”版本。

如果你的意思是几个值 X 1 , X 2 , ..., X N

如果您要添加 N 个数字,每个数字都在 0.0 和 1.0 之间,并且期望操作 X 1 + X 2 + ... 和 1.0 - X 1 ... 表现得像数学中的那样,这是一个替代解决方案。

每次获得一个新数 X i时,执行:X i ← 1.0 - (1.0 - X i )。从那时起只使用这个新的 X i值。此分配将稍微舍入 X i以便它在中间结果介于 0.0 和 1.0 之间的所有总和中表现良好。

编辑:对值 X 1,...,X N-1执行上述操作后,将 X N计算为 1 - X 1 - ... - X N-1。这种浮点计算将是精确的(尽管涉及浮点),因此您将有 X 1 + ... + X N = 1 精确。

于 2013-07-14T16:38:09.397 回答
5

从您的代码看来,您正在随机生成行星大气,大概是为了某种游戏或其他东西。无论如何,它的随机性让我相信它不需要太准确。

所以我建议你不要使用浮点数,只需使用ints 并上升到 100。然后你会得到你的确切总和。对于任何数学,你想在just cast中使用它们。

这不是一个选择吗?

如果您坚持使用浮点数,请继续阅读...

您使用浮点数的问题如下:

浮点数(在本例中为双精度数)表示如下:

在此处输入图像描述

这对应于一个double值:

在此处输入图像描述

所以,

你的号码是(1+M) * 2**(E)(其中E= e-offset

1+M始终在 1-2 范围内。

因此,我们在每对 2 的幂(正和负)之间有等间距的数字,并且随着指数的每次增加,数字之间的间距会加倍E

想一想,这意味着每个数字之间有一个恒定的可表示[(1,2),(2,4),(4,8), etc]数字间距。这也适用于二的负幂,所以:

0.5 - 1
0.25 - 0.5
0.125 - 0.25
0.0625 - 0.125
etc.

并且在每个范围内,都有相同数量的数字。这意味着,如果您取一个范围内的数字(0.25,0.5)并将其添加到范围内的一个数字上(0.5,1),那么您有 50% 的机会无法准确表示该数字。

如果您将两个指数相差 的浮点数相加D,则总和可精确表示的机会为 2 -D

如果你想表示 range 0-1,那么你必须非常小心你使用的浮点数(即强制N分数的最后一位为零,其中N是 的函数E)。

如果你沿着这条路线走,那么你最终会在范围顶部得到比底部更多的浮点数。

另一种方法是决定您希望能够接近零的程度。假设您想降低到 0.0001。

0.0001 = (1+M) * 2 E

日志2 (0.0001) = -13.28771...

所以我们将使用 -14 作为我们的最小指数。

然后要达到 1,我们只需将指数保留为 -1。

所以现在我们有 13 个范围,每个范围的值都是较低值的两倍,我们可以将它们相加而不必担心精度。

不过,这也意味着,顶部范围还有 213 个我们可以使用的值。这显然不行。

因此,在选择一个浮点数之后,将其到最接近的允许值 - 在这种情况下,我的意思是将最后 13 位设置为零,然后将其全部放入一个函数中,然后立即将其应用于您的数字你把它们弄出来rand

像这样的东西:

from ctypes import *

def roundf(x,bitsToRound):

    i = cast(pointer(c_float(x)), POINTER(c_int32)).contents.value

    bits = bin(i)

    bits = bits[:-bitsToRound] + "0"*bitsToRound

    i = int(bits,2)

    y = cast(pointer(c_int32(i)), POINTER(c_float)).contents.value

    return y

(图片来自维基百科)

于 2013-07-14T20:06:19.227 回答
1

最后,事实证明最简单的解决方案是改变问题。每次检查时将总和四舍五入到 5 位数的精度round(x,5)给出了足够的结果。

于 2013-07-20T16:19:41.137 回答
0

由于浮点数以二进制表示形式存储在机器中,因此总是存在无法精确表示的数字。如果您需要解决此限制,则必须使用一些使用自定义数据类型的数学库。

于 2013-07-14T16:36:20.967 回答
-2

浮点数由 2 的幂表示。来自 python 文档:“不幸的是,大多数十进制分数不能完全表示为二进制分数”

http://docs.python.org/2/tutorial/floatingpoint.html

编辑:也许不是实际尝试达到 1.0000000000000000000000,而是应该通过截断小数点后第三位的任何内容来确定可接受的错误级别。您可以相对确定加到 1 的值。使用此概念,您可以接受任何大于 0.999 且小于 1.001 的答案。

这可能并不完美,但它可能是解决问题的好方法。

于 2013-07-14T16:39:44.577 回答