在一些流行的桌面游戏中,存在一系列6个统计数据(也称为属性或能力分数)。它们是整数。出于这个问题的目的,它们的数字范围从7到18。
为了保持公平,有一个系统让人们“购买”更高的统计数据。这称为Point Buy。人们从最初的所有属性开始(在这种情况下为7)和一个积分池,然后他们可以花费来增加他们的属性。但是,它不是1-1比率。 18比17等贵得多。
我真正想要做的是,能够编写给定多个点的代码,并为每个属性提供成本,为我提供随机属性集(7到18之间的6个整数),提供的总分。理想情况下,我希望结果能够达到一致性。
为了使问题更容易思考,我可以提供一个从Pathfinder游戏系统中获取的示例。 (请注意,熟悉系统的人可能会注意到事情并不完全相同。我将其简化为计算机科学问题更有意义。)
所有属性从7开始,不能再降低。你有44个 花点。将属性提高1的成本为 如下:2,1,1,1,1,1,2,2,3,3,4或者,如果您更喜欢总费用, 2,3,4,5,6,7,9,11,14,17,21。
也欢迎任何类似问题的链接/资源。
答案 0 :(得分:1)
使用您的示例值来询问此问题的另一种方法是:您可以从集合{0,2,3,4,5,6,7,9,11中抽取多少种方式来替换6个数字,14,17,21}这样总和是44。
获得这些答案后,您可以统一选择一个答案,将它们随机播放到您的属性中,并为每个答案添加7个。
一些示例答案:
{21,21,2,0,0,0} {21,17,6,0,0,0} {21,17,4,2,0,0} {21,17,3,3,0,0} {21,17,2,2,2,0}
等
所以你选择其中一个,比如{21,21,2,0,0,0}。将其拖入您的6个属性(A到F),例如{21,0,0,2,21,0}。将其映射回您的值{18,7,7,8,18,7}。
注意,改组并不像听起来那么容易,有关于它的讨论有关于它的讨论,并且有一篇有趣的文章:http://www.cigital.com/papers/download/developer_gambling.php
这是正确的(我相信),但不一定有效(或漂亮),C ++来计算你的集合:
#include <vector>
#include <iterator>
#include <iostream>
typedef std::vector<int> Costs;
typedef std::vector<int> Attrs;
typedef std::vector<Attrs> Choices;
void gen(Choices& c, Attrs a, int sum, Costs costs, int attrs) {
if (sum < 0) { return; }
if (attrs < 1) {
if (sum == 0) {
c.push_back(a);
}
return;
}
auto cc = costs;
for (auto cost : costs) {
a.push_back(cost);
gen(c, a, sum - cost, cc, attrs - 1);
a.pop_back();
cc.erase(cc.begin());
}
}
Choices genChoices(int sum, const Costs& costs, int attrs) {
Choices allChoices;
gen(allChoices, Attrs(), sum, costs, attrs);
return allChoices;
}
int main(int, char*[]) {
const Costs costs { 21, 17, 14, 11, 9, 7, 6, 5, 4, 3, 2, 0 };
const int sum = 44;
const int attrs = 6;
auto choices = genChoices(sum, costs, attrs);
std::cout << choices.size() << "\n";
for (auto c : choices) {
std::copy(std::begin(c), std::end(c), std::ostream_iterator<Attrs::value_type>(std::cout, " "));
std::cout << "\n";
}
return 0;
}
使用g ++编译4.7.3:g ++ -std = c ++ 0x -Wall -Wextra attrs.cpp
您提供的示例中有280个。
答案 1 :(得分:0)
最简单的方法是随机生成所有值,然后使用给定的点数检查结果是否可行。如果它没有重新开始。
这可能看起来非常低效,当然是以某种方式,但对于实际应用来说,这是完全没问题的。例如,如果选择可行解决方案的机会是1/6,则预期尝试次数为6次,并且您需要超过40次尝试的机会小于1000次中的1次。
因此,除非你需要做大量的次数(至少> 1000),否则我会推荐这种方法。它易于编码,简短并适用于任何参数。
两次加速:
* *编辑刚刚意识到我错过了您所说的“所有积分消费”条件。这使问题变得更加困难,并且这种方法非常低效,因为只有很小一部分可能的配置是可行的。确切的动态取决于您的参数,因此如果您只需要少量样本,您可以尝试这种方法,看看它是否足够快。
答案 2 :(得分:0)
对于这里涉及的一般理论:可能的配置是对背包问题的修改。要统一采样,您可以迭代所有可行的配置并选择然后统一选择其中一个。我希望可行配置的数量相当小,所以如果你有一个快速找到它们的方法,那可以正常工作。
如果有太多可能的配置,因此无法枚举或统计它们,您就会遇到标准问题,即从您所拥有某些信息的分布中采样,但不是全部。我认为解决这个问题的标准方法是使用a Metropolis-Hastings algorithm或其他类型的马尔可夫链蒙特卡罗(另见Propp-Wilson Algorithm)。但是要使用这些方法,您必须在具有某些属性的可行状态之间构建转换函数。我试了一下,回合想不到一个。