Skip to content

Instantly share code, notes, and snippets.

@EnixCoda
Last active January 7, 2018 12:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EnixCoda/c530263f171dafd0470667cce942272a to your computer and use it in GitHub Desktop.
Save EnixCoda/c530263f171dafd0470667cce942272a to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Luck Money</title>
<style>
* {
margin: 6px;
}
body {
margin: 0;
}
button {
padding: 1em;
margin: .5em;
}
textarea.algo {
position: absolute;
top: 0;
left: 240px;
width: 450px;
height: 240px;
}
</style>
</head>
<body>
<form action="" id="condition">
<label>测试次数: <input type="number" name="cases" value="2000" min="0" max="10000"></label> <br>
<label>总金额: <input type="number" name="total" value="100" min="0"></label> <br>
<label>人数: <input type="number" name="people" value="10" min="0"></label> <br>
<label>最小值: <input type="number" name="min" value="6" min="0"></label> <br>
<label>最大值: <input type="number" name="max" value="12" min="0"></label> <br>
<label>
算法:
<select name="algo" id="algo">
<option value="1">1(随机因子比例分割)</option>
<option value="2">2(正态分布)</option>
<option value="3">3(正态分布,增加耗时)</option>
<option value="4">4(均衡随机数)</option>
<option value="5">5(溢出均分)</option>
<option value="6">6(强制平衡)</option>
</select>
</label>
<br>
</form>
<button type="button" name="progress" disabled>Google Charts准备中</button>
<textarea class="algo" disabled>算法区域</textarea>
<!--<button type="button" name="quick">快速测试</button>-->
<!--<div class="line chart"></div>-->
<div class="scatter chart"></div>
</body>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
function googleReady() {
var button = document.querySelector("[name='progress']");
button.innerHTML = "测试并统计";
button.disabled = false;
}
function sortNumber(a, b) {
return a - b;
}
function normalDistributionRandom() {
var v = 12;
var sum = 0, i = 0;
while (i++ < v) {
sum += Math.random();
}
return sum / v - 1 / 2; // -1/2 ~ 1/2
}
var luckyAlgo = {
1: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
var toDivide = total - people * min, i = 0,
seed, seeds = [], sumOfSeeds = 0;
while (i++ < people) {
seed = Math.random();
seeds.push(seed);
}
sumOfSeeds = seeds.reduce(function (prev, cur) {
return prev + cur
});
seeds = seeds.sort().reverse();
while (seeds[0] / sumOfSeeds > (max - min) / toDivide) {
seeds = seeds.map(function (cur) {
return Math.sqrt(cur);
});
sumOfSeeds = seeds.reduce(function (prev, cur) {
return prev + cur;
}, 0);
}
var sum = 0, oneLuck;
var luck = seeds.map(function (cur) {
oneLuck = min + (cur / sumOfSeeds) * toDivide;
sum += oneLuck;
return (oneLuck).toFixed(2);
});
return {
luckyMoney: luck,
sum: sum
};
},
2: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
var sum = 0, lucks = [], luck;
var avg = total / people;
var i = 0;
while (i++ < people) {
luck = avg + normalDistributionRandom() * (max - min);
luck = Math.max(min, luck);
luck = Math.min(max, luck);
if (total - sum - luck < (people - i) * min) luck = total - sum - (people - i) * min;
if (total - sum - luck > (people - i) * max) luck = total - sum - (people - i) * max;
lucks.push(luck);
sum += luck;
}
return {
luckyMoney: lucks.map(function (cur) {
return cur.toFixed(2);
}),
sum: sum
};
},
3: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
var sum = 0, lucks = [], luck;
var avg = total / people;
var i = 0;
while (i++ < people) {
do {
if (i === people) {
luck = total - sum;
break;
}
luck = avg + normalDistributionRandom() * (max - min);
} while (min > luck || max < luck || total - sum - luck < (people - i) * min || total - sum - luck > (people - i) * max);
lucks.push(luck);
sum += luck;
}
return {
luckyMoney: lucks.map(function (cur) {
return cur.toFixed(2);
}),
sum: sum
};
},
4: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
var i = 0;
var sum = 0, lucks = [], luck;
while (i++ < people) {
luck = min + Math.random() * (max - min);
if (total - sum - luck < (people - i) * min) luck = total - sum - (people - i) * min;
if (total - sum - luck > (people - i) * max) luck = total - sum - (people - i) * max;
sum += luck;
lucks.push(luck.toFixed(2));
}
return {
luckyMoney: lucks,
sum: sum
};
},
5: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
total *= 100;
min *= 100;
max *= 100;
var i = 0;
var sum = 0, rawLucks = [], lucks, luck;
while (i < people) {
luck = min + Math.round(Math.random() * (max - min));
sum += luck;
rawLucks.push(luck);
i++;
}
var fix;
i = 0;
while (i < people) {
fix = Math.round((total - sum) / (people - i));
rawLucks[i] += fix;
sum += fix;
i++;
}
rawLucks = rawLucks.sort(sortNumber).reverse();
var j, cut, overflow;
while (rawLucks[0] > max) {
overflow = rawLucks[0] - max;
rawLucks[0] -= overflow;
j = 0;
while (++j < people) {
cut = Math.round(overflow / (people - j));
if (rawLucks[j] < max) {
rawLucks[j] += cut;
overflow -= cut;
}
}
rawLucks = rawLucks.sort(sortNumber).reverse();
}
sum = rawLucks.reduce(function (prev, cur) {
return prev + cur;
});
i = 0;
while (sum > total) {
rawLucks[i++ % people]--;
sum--;
}
sum /= 100;
lucks = rawLucks.map(function (cur) {
return (cur / 100).toFixed(2);
});
if (sum.toFixed(2) != "100.00") console.log(sum);
return {
luckyMoney: lucks,
sum: sum
};
},
6: function (total, people, min, max) {
if (people * min > total || people * max < total) return false;
var sum = 0, lucks = [], luck;
total = parseInt(total * 100);
min = parseInt(min * 100);
max = parseInt(max * 100);
var i = 0;
while (i++ < people) {
lucks.push(min);
}
var toDivide = total - min * people;
var pos;
while (toDivide--) {
do {
pos = Math.floor(Math.random() * people);
} while (lucks[pos] >= max);
lucks[pos]++;
}
sum = lucks.reduce(function (prev, cur) {
return prev + cur;
});
lucks = lucks.map(function (cur) {
return (cur / 100).toFixed(2)
});
return {
luckyMoney: lucks,
sum: sum
};
}
};
var testCasesPassed = 0;
var testCases = document.querySelector("[name=cases]").value * 1;
var total = document.querySelector("[name=total]").value * 1;
var people = document.querySelector("[name=people]").value * 1;
var min = document.querySelector("[name=min]").value * 1;
var max = document.querySelector("[name=max]").value * 1;
google.charts.load('current', {'packages': ['corechart']});
google.charts.setOnLoadCallback(googleReady);
document.querySelector("textarea.algo").value = luckyAlgo[1].toString();
document.querySelector("select[name=algo]").addEventListener("change", function (e) {
document.querySelector("textarea.algo").value = luckyAlgo[e.srcElement.value].toString();
});
Array.prototype.forEach.call(document.getElementsByTagName("button"), function (cur) {
cur.addEventListener("click", function (e) {
testCases = document.querySelector("[name=cases]").value * 1;
total = document.querySelector("[name=total]").value * 1;
people = document.querySelector("[name=people]").value * 1;
min = document.querySelector("[name=min]").value * 1;
max = document.querySelector("[name=max]").value * 1;
document.querySelector("[name=progress]").disabled = true;
var algo = document.querySelector("select[name=algo]").value;
var lucky = [];
testCasesPassed = 0;
switch (e.target.name) {
case "progress":
withProgress();
break;
case "quick":
quick();
break;
default:
break;
}
function withProgress() {
var luck = luckyAlgo[algo](total, people, min, max);
testCasesPassed++;
if (!luck) {
alert("不合法的数据");
return;
}
if (testCasesPassed == 0 && lucky.length > 0) return;
lucky = lucky.concat(luck.luckyMoney);
if (lucky && (testCasesPassed % 100 == 0 || testCasesPassed == testCases)) {
if (testCasesPassed == testCases) {
document.querySelector("[name=progress]").disabled = false;
}
drawLuckyMoneyGraph(lucky);
}
if (testCasesPassed < testCases) {
setTimeout(withProgress, 0);
}
}
function quick() {
while (testCasesPassed < testCases) {
var luck = luckyAlgo[algo](total, people, min, max);
if (!luck) {
alert("不合法的数据");
return;
}
lucky = lucky.concat(luck.luckyMoney);
testCasesPassed++;
}
document.querySelector("[name=progress]").disabled = false;
drawLuckyMoneyGraph(lucky);
}
});
});
function drawLuckyMoneyGraph(lucky) {
if (!lucky || !google) return;
var counts = {};
lucky.forEach(function (cur) {
counts[cur] = counts[cur] === undefined ? 1 : counts[cur] + 1;
});
var _counts = [];
for (var amount in counts) {
_counts.push([amount, counts[amount]]);
}
counts = _counts.sort(function (a, b) {
a = a[0] * 1;
b = b[0] * 1;
if (a > b) return -1;
if (a < b) return 1;
return 0;
});
var data = new google.visualization.arrayToDataTable([["amount", "count"]].concat(counts));
var options = {
title: testCasesPassed + '次LuckyMoney分配情况统计',
width: 800,
height: 300
};
// var lineChart = new google.visualization.LineChart(document.querySelector(".line.chart"));
// lineChart.draw(data, options);
var scatterChart = new google.visualization.ScatterChart(document.querySelector(".scatter.chart"));
scatterChart.draw(data, options);
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment