docs: add distribution statistics about fortune events #29
BIN
docs/bad_fortune_statistics.png
Normal file
BIN
docs/bad_fortune_statistics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
26
docs/fortune_statistics.md
Normal file
26
docs/fortune_statistics.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Distribution of fortune
|
||||
### 2000 IPs for 365 days, two groups in total
|
||||
|
||||
| Fortune Status | Percentage (1st time) | Percentage (2nd time) |
|
||||
|-----------------|------------------------|------------------------|
|
||||
| 大吉 | 20.33% | 20.30% |
|
||||
| 中吉 | 14.37% | 14.34% |
|
||||
| 小吉 | 10.57% | 10.59% |
|
||||
| 吉 | 14.49% | 14.44% |
|
||||
| 末吉 | 10.14% | 10.24% |
|
||||
| 中平 | 14.29% | 14.40% |
|
||||
| 凶 | 8.64% | 8.60% |
|
||||
| 大凶 | 7.17% | 7.09% |
|
||||
|
||||
# Distribution statistics of daily fortune events
|
||||
|
||||
Statistical method: The sum of the number of fortune events that occurred for 2,000 random IPs on the same day.
|
||||
|
||||
The x-axis is the index value and the y-axis is the number of times.
|
||||
|
||||
| 宜 (Good Fortune) | 忌 (Bad Fortune) |
|
||||
|------------------------------------------------|----------------------------------------------|
|
||||
|  |  |
|
||||
|
||||
|
||||
[Statistics code](./main.js)
|
||||
@@ -1,20 +0,0 @@
|
||||
# 運勢出現分佈
|
||||
|
||||
大吉: 20.33%
|
||||
中吉: 14.37%
|
||||
小吉: 10.57%
|
||||
吉: 14.49%
|
||||
末吉: 10.14%
|
||||
中平: 14.29%
|
||||
凶: 8.64%
|
||||
大凶: 7.17%
|
||||
|
||||
大吉: 20.30%
|
||||
中吉: 14.34%
|
||||
小吉: 10.59%
|
||||
吉: 14.44%
|
||||
末吉: 10.24%
|
||||
中平: 14.40%
|
||||
凶: 8.60%
|
||||
大凶: 7.09%
|
||||
|
||||
BIN
docs/good_fortune_statistics.png
Normal file
BIN
docs/good_fortune_statistics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
284
docs/main.js
Normal file
284
docs/main.js
Normal file
@@ -0,0 +1,284 @@
|
||||
var fs = require('fs')
|
||||
|
|
||||
|
||||
const goodFortunes = [
|
||||
{
|
||||
"event": "睡覺",
|
||||
"description": "品質良好,精神煥發"
|
||||
},
|
||||
{
|
||||
"event": "做家務",
|
||||
"description": "整潔使人心情愉悅"
|
||||
},
|
||||
{
|
||||
"event": "冥想",
|
||||
"description": "平靜心靈,緩解焦慮"
|
||||
},
|
||||
{
|
||||
"event": "攝影",
|
||||
"description": "捕捉到美好瞬間"
|
||||
},
|
||||
{
|
||||
"event": "喝咖啡",
|
||||
"description": "精力充沛燃燒脂肪"
|
||||
},
|
||||
{
|
||||
"event": "朋友聚會",
|
||||
"description": "充滿歡笑和美好回憶"
|
||||
},
|
||||
{
|
||||
"event": "體育鍛鍊",
|
||||
"description": "能量滿滿,效果顯著"
|
||||
},
|
||||
{
|
||||
"event": "出遊" ,
|
||||
"description": "好天氣,好心情"
|
||||
},
|
||||
{
|
||||
"event": "吃大餐",
|
||||
"description": "聯絡感情"
|
||||
},
|
||||
{
|
||||
"event": "逛書店",
|
||||
"description": "新書上架,打折推銷"
|
||||
},
|
||||
{
|
||||
"event": "學新技能",
|
||||
"description": "快速上手"
|
||||
},
|
||||
{
|
||||
"event": "唱歌",
|
||||
"description": "被星探發掘"
|
||||
},
|
||||
{
|
||||
"event": "上課",
|
||||
"description": "整天不累,100% 消化"
|
||||
},
|
||||
{
|
||||
"event": "洗澡",
|
||||
"description": "重獲能量"
|
||||
},
|
||||
{
|
||||
"event": "請教問題",
|
||||
"description": "問題皆獲高人指點"
|
||||
},
|
||||
{
|
||||
"event": "網購",
|
||||
"description": "心儀商品皆促銷"
|
||||
},
|
||||
{
|
||||
"event": "放假",
|
||||
"description": "休息充電,明日再戰"
|
||||
},
|
||||
{
|
||||
"event": "早睡",
|
||||
"description": "好夢連連"
|
||||
},
|
||||
{
|
||||
"event": "早起",
|
||||
"description": "朝氣蓬勃,神采飛揚"
|
||||
},
|
||||
{
|
||||
"event": "發文章",
|
||||
"description": "瀏覽數暴增"
|
||||
},
|
||||
{
|
||||
"event": "點外賣",
|
||||
"description": "準時到達,新鮮好吃"
|
||||
},
|
||||
{
|
||||
"event": "做善事",
|
||||
"description": "積善成福"
|
||||
},
|
||||
{
|
||||
"event": "散步",
|
||||
"description": "空氣良好,放鬆身心"
|
||||
}
|
||||
];
|
||||
|
||||
const badFortunes = [
|
||||
{
|
||||
"event": "體育鍛鍊",
|
||||
"description": "不慎受傷"
|
||||
},
|
||||
{
|
||||
"event": "攝影",
|
||||
"description": "照片全消失"
|
||||
},
|
||||
{
|
||||
"event": "出遊",
|
||||
"description": "天氣不晴朗"
|
||||
},
|
||||
{
|
||||
"event": "吃大餐",
|
||||
"description": "被要求請客"
|
||||
},
|
||||
{
|
||||
"event": "學新技能",
|
||||
"description": "屢試不爽,始終不懂"
|
||||
},
|
||||
{
|
||||
"event": "唱歌",
|
||||
"description": "嗓子發炎"
|
||||
},
|
||||
{
|
||||
"event": "洗澡",
|
||||
"description": "水溫不穩"
|
||||
},
|
||||
{
|
||||
"event": "請教問題",
|
||||
"description": "疑難雜症,均無解答"
|
||||
},
|
||||
{
|
||||
"event": "網購",
|
||||
"description": "錯過促銷"
|
||||
},
|
||||
{
|
||||
"event": "放假",
|
||||
"description": "隔日工作量倍增"
|
||||
},
|
||||
{
|
||||
"event": "晚睡",
|
||||
"description": "失眠,明日精神渙散"
|
||||
},
|
||||
{
|
||||
"event": "晚起",
|
||||
"description": "整天都不順"
|
||||
},
|
||||
{
|
||||
"event": "發文章",
|
||||
"description": "搜索枯腸,不知所云"
|
||||
},
|
||||
{
|
||||
"event": "點外賣",
|
||||
"description": "路況壅塞,餐點冷掉"
|
||||
},
|
||||
{
|
||||
"event": "喝咖啡",
|
||||
"description": "晚上失眠"
|
||||
},
|
||||
{
|
||||
"event": "散步",
|
||||
"description": "被害蟲咬傷"
|
||||
}
|
||||
];
|
||||
|
||||
|
fetch fetch `goodFortunes` and `badFortunes` from [here](https://github.com/LifeAdventurer/generators/blob/main/fortune_generator/json/fortune.json)
|
||||
const badLen = badFortunes.length;
|
||||
const goodLen = goodFortunes.length;
|
||||
let num = null;
|
||||
const dates = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 30];
|
||||
const statusLen = 8;
|
||||
var buckets = {};
|
||||
let day = 0;
|
||||
var run_cnt = 0;
|
||||
|
||||
while (run_cnt != 2000) {
|
||||
let n1 = parseInt(Math.random() * 255 + 1);
|
||||
let n2 = parseInt(Math.random() * 255 + 1);
|
||||
let n3 = parseInt(Math.random() * 255 + 1);
|
||||
let n4 = parseInt(Math.random() * 255 + 1);
|
||||
|
||||
if (!check_ip_valid(n1, n2, n3, n4)) continue;
|
||||
|
||||
let index = `${n1}.${n2}.${n3}.${n4}`;
|
||||
// buckets[`${n1}.${n2}.${n3}.${n4}`] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
buckets[`${n1}.${n2}.${n3}.${n4}`] = [0, 0, 0, 0];
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
for (let j = 1; j <= dates[i - 1]; j++) {
|
||||
day %= 7;
|
||||
run(2023, i, j, day, [n1, n2, n3, n4]);
|
||||
day++;
|
||||
}
|
||||
}
|
||||
|
||||
run_cnt++;
|
||||
}
|
||||
|
||||
fs.writeFile('./res.txt', JSON.stringify(buckets), err => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
function check_ip_valid(n1, n2, n3, n4) {
|
||||
if (n1 > 255 || n2 > 255 || n3 > 255 || n4 > 255) return false;
|
||||
|
||||
// private network
|
||||
if (n1 === 10) return false;
|
||||
|
||||
// Carrier-grade NAT
|
||||
if (n1 == 100 && n2 == 64) return false;
|
||||
|
||||
// localhost
|
||||
if (n1 === 127 && n2 === 0 && n3 === 0) return false;
|
||||
|
||||
// link-local address
|
||||
if (n1 == 169 && n2 == 254) return false;
|
||||
|
||||
// private network
|
||||
if (n1 === 172) if (n2 >= 16 && n2 <= 31) return false;
|
||||
|
||||
if (n1 === 192) {
|
||||
if (n2 === 168) return false; // private network
|
||||
if (n2 === 0 && n3 === 0) return false; // IANA RFC 5735
|
||||
if (n2 === 0 && n3 === 2) return false; // TEST-NET-1 RFC 5735
|
||||
if (n2 === 88 && n3 === 99) return false; // 6to4
|
||||
}
|
||||
|
||||
if (n1 == 198) {
|
||||
if (n2 == 18) return false; // RFC 2544
|
||||
if (n2 == 51 && n3 == 100) return false; // TEST-NET-2 RFC 5735
|
||||
}
|
||||
|
||||
if (n1 == 203 && n3 == 113) return false; // TEST-NET-3 RFC 5735
|
||||
|
||||
// class D network
|
||||
if (n1 == 224) return false;
|
||||
|
||||
// class E network
|
||||
if (n1 == 255) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// calculate hash and write result
|
||||
function run(year, month, date, day, ip) {
|
||||
let num = ip;
|
||||
let index = `${ip[0]}.${ip[1]}.${ip[2]}.${ip[3]}`;
|
||||
let d = new Date(year, month, date);
|
||||
|
||||
// original hash function
|
||||
let hashDate = Math.round(Math.log10(year * ((month << (Math.log10(num[3]) + day - 1)) * (date << Math.log10(num[2] << day)))));
|
||||
let seed1 = (num[0] >> hashDate) * (num[1] >> Math.min(hashDate, 2)) + (num[2] << 1) * (num[3] >> 3) + (date << 3) * (month << hashDate) + ((year * day) >> 2);
|
||||
let seed2 = (num[0] << (hashDate + 2)) * (num[1] << hashDate) + (num[2] << 1) * (num[3] << 3) + (date << (hashDate - 1)) * (month << 4) + (year >> hashDate) + ((date * day) >> 1);
|
||||
|
||||
// decide the status
|
||||
let status_index = ((seed1 + seed2) % statusLen + statusLen) % statusLen;
|
||||
// buckets[index][Math.max(status_index - parseInt(day / Math.PI), 0)]++; // old hash
|
||||
// buckets[index][status_index]++; // current hash
|
||||
|
||||
// make sure the events won't collide
|
||||
let set = new Set();
|
||||
let l1 = (seed1 % goodLen + goodLen) % goodLen;
|
||||
let l2 = (((seed1 << 1) + date) % goodLen + goodLen) % goodLen;
|
||||
|
||||
while (l1 == l2) {
|
||||
l2 = (l2 + 1) % goodLen;
|
||||
}
|
||||
|
||||
set.add(goodFortunes[l1].event);
|
||||
set.add(goodFortunes[l2].event);
|
||||
|
||||
let r1 = (((seed1 >> 1) + (d.getMonth() << 3)) % badLen + badLen) % badLen;
|
||||
while (set.has(badFortunes[r1].event)) {
|
||||
r1 = (r1 + 2) % badLen;
|
||||
}
|
||||
set.add(badFortunes[r1].event);
|
||||
let r2 = ((((((seed1 << 3) + (d.getFullYear() >> 5) * (date << 2)) % badLen) * seed2) >> 6) % badLen + badLen) % badLen;
|
||||
while (set.has(badFortunes[r2].event)) {
|
||||
r2 = (r2 + 1) % badLen;
|
||||
}
|
||||
|
||||
// write l1, l2, r1, r2
|
||||
buckets[index][0] = l1;
|
||||
buckets[index][1] = l2;
|
||||
buckets[index][2] = r1;
|
||||
buckets[index][3] = r2;
|
||||
}
|
||||
Reference in New Issue
Block a user
remove useless loops