问题:假设 有三个产品,X,Y,Z,单价分别为,1.0元,2.4元,4.5元,总销售额为2347元,怎么随机分摊给这三个产品,让结果应该是随机性的分摊,不能定死,而且分摊完成后,每个产品乘以单价然后全部加起来要等于总销售额【如果有误差,也只能在[-100,100]之间】。奈何能力有限,请能人抬手相助,谢谢!
方法一:
假设产品集为 S={P1,P2,...,Pn},总销售额为 C,产品单价分别为 Q1,Q2,...,Qn。产品数为 M1,M2,...,Mn。
注意,Random(...) 并不一定是均匀分布,也可以是别的分布形式。
方法二:
利用等价交换的理念。
方法三:
基于方法 2,假设产品数目为 N,设置 N 个节点,每个节点对应一种商品,每个节点的商品价值总量都约等于总销售额。接着让节点之间自由贸易(等值 [可以设定是否允许有一定误差] 交换)。在足够的贸易次数后,每个节点都是一个解。
方法四:
一件一件增加,随机增加一件商品,其数目增加1,计算此时的销售总额,低于目标,继续重复,直到满足。又或者从销售额开始随机减去商品。
1,谢谢你啊
2,你好厉害
3,你的方法里,我觉得还是有个问题,就是你尽量做到了随机分摊,可是可能存在有些产品被分到了数值较大的金额,而有些可能会是零,或者数额比较少,看起来几种产品的数额比较悬殊。像这种情况,怎么处理比较适合。由于我水平一般,你方法二和三虽然理解,不过实际操作还是没什么头绪,我想用方法四,这样可以尽可能的吧误差控制在我想要的范围内,但就怕随机挑到的只是其中几个产品,而又些则很少被挑到,导致比例失衡,账面看起来太有端倪。
@越过: 我不知道你如何理解我的。方法4就是平均随机的呀,他就是模拟实际的买卖,不过每次买卖都是随便选的商品,不会考虑更多因素的。
方法一光想没用的。概率从来不是想当然的,你可以看我这篇博文http://www.cnblogs.com/Pandaman/p/random-itertools.html,因此他是不是平均分布的,你试试就好啦。
关于交易,很简单,5个Y,就是2.4*5=12=1*12,可以换12个产品X。也可以换2个Z+3个X。方法之间不是死的。1234可以互相借用嘛,综合使用。
譬如可以有方法5。你的假设XYZ,单价是{1,2.4,4.5},它们的最小公倍数是36,那求出所有可能的组合,每次增减36元,随机选择其中一种组合。那么方法23的交易规模也可以固定为每次36元。方法5增减的也不一定是产品数,可以是金额数。而且方法5本身又是原问题的子问题。原问题的分配结果太多,子问题容易枚举。关于方法5,假设一种简单的情况
{X,Y,Z},单价{1,2,3},最小公倍数为6,可能为(1,1,1),(6,0,0),(0,0,2),(2,2,0),(3,0,1),(0,3,0)等情况。
还有题主,随机性分摊不等于平均分摊,就算平均,也有件数和价值总额两种把。如果是价值总额平均
那就总价/产品数。那就是平均价值总额,所有产品都在这个值附近波动。
如果是件数平均,那你先求出总价/平均单价,得到平均的件数,所有产品都在附近取值。
最后2种方法能够快速得到结果(荐),可惜可能会超过误差。并且附近(这个附近可以是人为指定范围内的平均随机取值,也可以是正态分布,推荐正态分布)一词需要人为定义。至于超过误差,之前说的方法1-5都可以用嘛。譬如多了,我就随便剔除一些。少了就随便增加一下。
嘛,总之,我觉得方法4最好理解,也最简单。不过其他方法也有闪光点的。譬如方法1,可以搜索到所有的解哟。
@越过:
你的方法里,我觉得还是有个问题,就是你尽量做到了随机分摊,可是可能存在有些产品被分到了数值较大的金额,而有些可能会是零,或者数额比较少,看起来几种产品的数额比较悬殊。
我想说,你抛一枚硬币,不会出现连续5次向上吗?就算是方法4,也会出现某些产品相当多,某些没有。尽管在方法4中这种情况出现的概率是极小的(只要总销售额较之产品种数或者单价尽可能多)。
方法1完全可以改进。你不用一次拿全部的销售额进行方法1的步骤,你把他分为多个子过程。每个过程都用方法1的方法。
另外,你问题的答案,方法实在太多,先不说我已经举了6个方法。就算你再叫我说6个都没问题。重要的是,那么多方法,你要找到最适合自己的要求的方法。
@越过: 方法4,写了一个程序
1 from random import * 2 3 n, C, Q = 3, 2347, [1, 2.4, 4.5] 4 S = [i for i in range(n)] 5 M = [0 for i in range(n)] 6 7 while(sum([M[i]*Q[i] for i in range(n)])<C): 8 M[choice(S)] += 1 9 10 print(M,sum([M[i]*Q[i] for i in range(n)])) 11 12 # 运行结果:[307, 301, 293] 2347.9
注意:每次运行结果不同!
其实 {2347/[(1+2.4+4.5)/3]}/3=2347/(1+2.4+4.5)=297。
即总销售金额除以单价总和,就是每件商品大概数额。
@巛熊猫人灬:
1,再次感谢你的耐心解答,。
2,程度不一样,可能我理解能力相对较弱,但我并没有想当然的,就算是随机后出现悬殊情况,也是一中随机结果,这个我知道的,我只是因为我目前遇到的事情,不能存在这种悬殊情况而已,你第四种方法也是随机的,我知道啊,我没说不是啊。
3,我实际碰到的问题是要把大笔的销售额平摊到分摊到几个产品上【为了把销售数量抬上去,所以不能有的分摊下数量为0,有的很大】,本来是人工手动去调,这个产品分一些,那个产品分一些,我实际的误差其实要控制在1块钱内。我是打算用方法4试一试【能力有限,挑比较容易操作的试试】。
@越过: 方法四如果用我给的那段代码的误差只能保证低于最高的单价。具体还可以调整。计算出销售额C*,误差e=C*-C,误差为正,则随机去掉多过平均件数的产品,并且使得误差的绝对值E尽可能降低,如果误差为负数,随机增加低于平均件数的产品,并且使得误差的绝对值E尽可能降低。大概就是这样了。也可以用别的方法调整。
@巛熊猫人灬:
1,再再次感谢你的耐心回答
2,我确实在想着,用方法四,最后误差可能会在0-最大单价之间,我现在打算把产品的最大单价设为20,就是最大的那个产品只能是20元内,这样,最后的误差就是20元内,所以,我现在在想办法怎么把这个20元内的误差给降低到1元内··········
@越过:
适当改一下判断部分,在进入最大单价误差范围内,遇到会使误差增加的情况,就搜索是否有使误差降低的方法(譬如此时直接用最小单价的)。最后的误差就一定小于最小的单价。
不过如果你商品最低的单价也很高,远大于1。那用上面的方法进行调整也不一定好使。我觉得直接上遗传算法就OK了。粗暴无脑。就是要一点内存和时间。
还有一种简单有效的方法,就是保证有正有负的误差,求平均(缺点是需要处理含有小数的情况),或者把问题分割为多部分,每部分的误差有正有负。譬如4444的总销售额。就可以分为2222与2222两部分。一个取大于一个取小于(譬如我程序里,记住超过目标销售额之前的取值)。不就可以抵消误差了吗?你既可以把4444分为4分1111,2正2负求和。 还可以分为10份1111,选最小误差的4份求和。
其实既然你对误差有那么高的要求,你就直接求出误差最小的解!这个解肯定不是唯一的,但是误差的要求明显更加苛刻。那么直接用我说的贸易法则就好了。把数目较多的货物换成较少的。不就好了。这样在保证误差的前提下,随机性和均衡都得到满足。
鸡兔同笼
1,谢谢你的回答
2,我觉得我的问题和你讲的这个可能情况是有出入的,“鸡兔同笼”可能不能够解决我的问题,因为我要的是随机的分摊,如果用“鸡兔同笼”的方式去解决,那很可能产生我说的那个分配悬殊的问题
@越过: 如果某商品的件数大于钱数除以该价值后所得件数的百分之50,说明该商品太多,拿出一定数量的商品,得到这些商品的价值,继续执行该程序,注意设定一个值,防止程序死循环。
@羽商宫: 如果是这样的话,那得反复做这个动作好多次,同时变得像在做平均分摊一样,如果能解决分配悬殊问题,用上面那位的方式就蛮好的
就直接随机分, 误差超出区间就重来呗。