Last active
January 7, 2018 12:18
-
-
Save EnixCoda/c530263f171dafd0470667cce942272a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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