草庐IT

javascript - 我如何在一系列百分比上最佳地分配值?

coder 2024-07-23 原文

假设我有以下代码:

arr = [0.1,0.5,0.2,0.2]; //The percentages (or decimals) we want to distribute them over.
value = 100; //The amount of things we have to distribute
arr2 = [0,0,0,0] //Where we want how many of each value to go

要找出如何在数组中平均分配一百个很简单,这是一个例子:

0.1 * 100 = 10
0.5 * 100 = 50
...

或者使用 for 循环:

for (var i = 0; j < arr.length; i++) {
    arr2[i] = arr[i] * value;
}

但是,假设每个计数器都是一个对象,因此必须是完整的。我怎样才能平等地(尽可能多地)以不同的值(value)分配它们。假设值变为 12。

0.1 * 12 = 1.2
0.5 * 12 = 6
...

当我需要整数时如何处理小数?四舍五入意味着我可能没有所需的 12 件。

正确的算法会 -

通过值数组进行输入/迭代(对于此示例,我们将使用上面定义的数组。

将它变成一组整数值,加在一起等于值(为此等于 100)

输出一个值数组,在这个例子中它看起来像 [10,50,20,20](这些值加起来是 100,这是我们需要将它们加起来的值并且也是整数) .

如果任何值不完整,它应该使它完整,这样整个数组仍然加起来等于所需的值 (100)。

TL;DR 在数组上分配值并尝试将它们转换为整数时处理小数

注意 - 如果将其发布在不同的 stackoverflow 网站上,我需要的是编程,但实际问题可能会使用数学来解决。另外,我不知道如何表达这个问题,这使得谷歌搜索变得异常困难。如果我错过了一些非常明显的事情,请告诉我。

最佳答案

您应该在分配所有值时使用已知会均匀分布舍入的舍入对所有值进行舍入。最后,最后一个值将被分配不同的值以将总和四舍五入为 1

让我们慢慢开始,否则事情会变得非常困惑。首先,让我们看看如何分配最后一个值以获得所需的总值。

// we will need this later on
sum = 0;

// assign all values but the last
for (i = 0; i < output.length - 1; i++)
{
    output[i] = input[i] * total;
    sum += output[i];
}

// last value must honor the total constraint
output[i] = total - sum;

最后一行需要一些解释。 i 将比 for(..) 循环中最后允许的 int 多一个,因此它将是:

output.length - 1 // last index

我们分配的值将使所有元素的 sum 等于 total。我们已经在赋值期间一次性计算了总和,因此不需要再次迭代元素来确定总和。

接下来,我们将解决舍入问题。让我们简化上面的代码,以便它使用一个我们将在稍后详细说明的函数:

sum = 0;
for (i = 0; i < output.length - 1; i++)
{
    output[i] = u(input[i], total);
    sum += output[i];
}

output[i] = total - sum;

如您所见,除了 u() 函数的引入外,没有任何变化。现在让我们专注于此。

u() 的实现有多种方法。

DEFINITION
u(c, total) ::= c * total

根据这个定义,您得到的结果与上面相同。它既精确又好,但正如您之前所问,您希望这些值是自然数(例如整数)。因此,虽然对于实数这已经是完美的,但对于自然数我们必须对其进行四舍五入。假设我们对整数使用简单的舍入规则:

[ 0.0, 0.5 [  => round down
[ 0.5, 1.0 [  => round up

这是通过以下方式实现的:

function u(c, total)
{
    return Math.round(c * total);
}

当您不走运时,您可能会向上舍入(或向下舍入)太多值,以至于最后一次值修正不足以满足总约束条件,而且通常,所有值似乎都偏离太多。这是一个众所周知的问题,存在一个在 2D 和 3D 空间中绘制线条的多维解决方案,称为 Bresenham algorithm .

为了让事情变得简单,我将在这里向您展示如何在一维中实现它(这是您的情况)。

让我们首先讨论一个术语:余数。这是你四舍五入后剩下的。它被计算为你想要的和你真正拥有的之间的差异:

DEFINITION
WISH ::= c * total
HAVE ::= Math.round(WISH)
REMAINDER ::= WISH - HAVE

现在想想。剩下的就像你从一张纸上剪下一个形状时丢弃的那张纸。剩下的那张纸还在那里,但你把它扔掉了。取而代之的是,只需将它添加到下一个切口,这样就不会浪费它:

WISH ::= c * total + REMAINDER_FROM_PREVIOUS_STEP
HAVE ::= Math.round(WISH)
REMAINDER ::= WISH - HAVE

这样您就可以保留错误并将其转移到计算中的下一个分区。这称为摊销错误。

这是 u() 的分摊实现:

// amortized is defined outside u because we need to have a side-effect across calls of u
function u(c, total)
{
    var real, natural;

    real = c * total + amortized;
    natural = Math.round(real);
    amortized = real - natural;

    return natural;
}

根据您的意愿,您可能希望有另一个舍入规则,如 Math.floor()Math.ceil()

我建议您使用 Math.floor(),因为它已被证明对于总约束是正确的。当您使用 Math.round() 时,您将获得更平滑 的摊销,但您可能会面临最后一个值不为正的风险。你可能会得到这样的结果:

[ 1, 0, 0, 1, 1, 0, -1 ]

只有当ALL VALUES 远离 0 时,您才可以确信最后一个值也将为正。因此,对于一般情况,Bresenham 算法 将使用 flooring,从而导致最后一个实现:

function u(c, total)
{
    var real, natural;

    real = c * total + amortized;
    natural = Math.floor(real); // just to be on the safe side
    amortized = real - natural;

    return natural;
}

sum = 0;
amortized = 0;
for (i = 0; i < output.length - 1; i++)
{
    output[i] = u(input[i], total);
    sum += output[i];
}

output[i] = total - sum;

显然,inputoutput 数组必须具有相同的大小并且 input 中的值必须是一个分区(总和为 1) .

这种算法在概率和统计计算中很常见。

关于javascript - 我如何在一系列百分比上最佳地分配值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27330331/

有关javascript - 我如何在一系列百分比上最佳地分配值?的更多相关文章

  1. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  6. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  7. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  8. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  9. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  10. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

随机推荐