python 使用给定的比率细分整数而不产生浮点数

1rhkuytd  于 2023-02-21  发布在  Python
关注(0)|答案(4)|浏览(123)

我需要使用给定的比率[0.55, 0.45]细分给定数量的项目(假设10个),这里的结果应该是6:4或5:5。通常的方法[0.55*10, 0.45*10]将导致[6, 5](11,而不是10)。
另一个例子:使用比率除以7:[0.36, 0.44, 0.07, 0.07, 0.03, 0.03],其理想地应当产生类似于[3, 3, 1, 0, 0, 0][3, 3, 0, 1, 0, 0]的值。
解决这个问题的好办法是什么?

l2osamch

l2osamch1#

下面是我对这个问题的尝试:)最困难的部分是反转排序操作并将其与结果匹配...如果您不需要保持原始的比率顺序,那么您可以删除最后一个函数的一部分。

def scale_ratio(ratios: list) -> list:
    sum_ = sum(ratios)
    return [x/sum_ for x in ratios]

def ratio_breakdown_recursive(x: int, ratios: list) -> list:
    top_ratio = ratios[0]
    part = round(x*top_ratio)
    if x <= part:
        return [x]
    x -= part
    return [part] + ratio_breakdown_recursive(x, scale_ratio(ratios[1:]))

def ratio_breakdown(x: int, ratios: list) -> list:
    sorted_ratio = sorted(ratios, reverse=True)
    assert(round(sum(ratios)) == 1)
    sorted_result = ratio_breakdown_recursive(x, sorted_ratio)
    assert(sum(sorted_result) == x)
    # Now, we have to reverse the sorting and add missing zeros
    sorted_result += [0]*(len(ratios)-len(sorted_result))
    numbered_ratios = [(r, i) for i, r in enumerate(ratios)]
    sorted_numbered_ratios = sorted(numbered_ratios, reverse=True)
    combined = zip(sorted_numbered_ratios, sorted_result)
    combined_unsorted = sorted(combined, key=lambda x: x[0][1])
    unsorted_results = [x[1] for x in combined_unsorted]
    return unsorted_results

结果:

ratio_breakdown(7, [0.36, 0.44, 0.07, 0.07, 0.03, 0.03])
[3, 3, 1, 0, 0, 0]
ratio_breakdown(10, [0.55, 0.45])
[6, 4]
ratio_breakdown(16, [0.16, 0.47, 0.13, 0.24])
[2, 8, 2, 4]

编辑:这是Python3。

r3i60tvu

r3i60tvu2#

我建议你用另一个数组,我是Python的初学者。
下面是你的例子的代码(我相信你可以简单地修改它):

a = [0.36, 0.44, 0.07, 0.07, 0.03, 0.03]

numberItem = 7
remainder = numberItem

b = [0,0,0,0,0,0]

for i in range(0,6):
    b[i] = round(a[i]*numberItem)
    if (b[i] > remainder) or (b[i] == 0):
        b[i] = remainder
        remainder = 0
    else:
            remainder = remainder - b[i]
    print(b[i])

在这个范围内,你不能有比指定的更多的项。如果比率数组从大到小排序会更好。

1qczuiv0

1qczuiv03#

下面是@maciek算法的非递归NumPy实现:

import numpy as np

def split_integer_into_parts(x: int, ratios: list) -> np.ndarray:

    ratios = np.array(ratios, dtype=float)

    assert x >= 0
    assert (ratios >= 0).all()
    assert ratios.sum() > 0

    # sort ratios
    sort_idx = np.argsort(-ratios)
    ratios = ratios[sort_idx]

    # compute fractions of the remainders
    ratios_cumsum = np.cumsum(ratios[::-1])[::-1]
    fracs = np.divide(ratios, ratios_cumsum, out=np.ones_like(ratios), where=(ratios_cumsum != 0))

    # split integer into parts
    remainder = x
    parts = np.zeros_like(fracs, dtype=int)
    for i, frac in enumerate(fracs):
        parts[i] = round(remainder * frac)
        remainder -= parts[i]

    assert parts.sum() == x

    # unsort parts
    parts = parts[np.argsort(sort_idx)]

    return parts
o0lyfsai

o0lyfsai4#

用R书写的接受答案

我一直在寻找这个问题/答案,但是我在R中工作,所以我用R为任何通过这种方式的人重写了@Maciek的答案,我尽可能地使它接近Python中的原始答案。

library(dplyr)

scale_ratio <- function(ratios){
  sum_ <- sum(ratios)
  return(ratios/sum_)
}

ratio_breakdown_recursive <- function(x, ratios){
  top_ratio <- ratios[1]
  part <- round(x*top_ratio)
  if (x <= part){
    return(x)
  }
  x <- (x - part)
  c(part, ratio_breakdown_recursive(x, scale_ratio(ratios[2:length(ratios)])))
}

ratio_breakdown <- function(x, ratios){
  x <- x[1]
  sorted_ratio = sort(ratios, decreasing = TRUE)
  stopifnot(round(sum(ratios)) == 1)
  sorted_result = ratio_breakdown_recursive(x, sorted_ratio)
  stopifnot(sum(sorted_result) == x)
  # Now, we have to reverse the sorting and add missing zeros
  sorted_result <- append(sorted_result, rep(0, length(ratios) - length(sorted_result)))
  numbered_ratios <- data.frame(ratios, seq_along(ratios))
  sorted_numbered_ratios <- arrange(numbered_ratios, desc(ratios))
  combined <- cbind(sorted_numbered_ratios, sorted_result)
  combined_unsorted <- arrange(combined, seq_along.ratios.)
  unsorted_results <- combined_unsorted[,3]
  return(unsorted_results)
}
> ratio_breakdown(7, c(0.36, 0.44,0.07,0.07,0.03,0.03))
[1] 3 3 0 1 0 0
> ratio_breakdown(10, c(0.55,0.45))
[1] 6 4
> ratio_breakdown(16, c(0.16,0.47,0.13,0.24))
[1] 2 8 2 4

我意识到第一个答案返回的顺序与@Maciek的答案不同,但就我的目的而言,这是有效的,如果有人能改进我的R代码,请自便--我很乐意学习。

相关问题