PageSourceSearch

https://sparkmxy.github.io/iching/core.mjs

js sparkmxy.github.io collected 2026-10-03 09:58:38 UTC 2,607 bytes, 71 lines download raw bytes

1/**
2 * The yarrow-stalk procedure in Zhu Xi's 筮仪, 原本周易本义, 卷末下.
3 * Source: https://zh.wikisource.org/wiki/原本周易本義_(四庫全書本)/卷末下
4 *
5 * This is an explicit mathematical model of the text's remainder classes.
6 * For each change, the four possible LEFT remainders are equally likely.
7 * A second draw chooses uniformly among valid splits in that class. This
8 * produces a real, inspectable split while preserving the text's 3:1 first
9 * change and 1:1 later changes. Uniformly selecting all possible pile sizes
10 * would give slightly different probabilities, so we do not claim to do so.
11 * drawInt(bound) must return an unbiased integer in [0, bound).
12 */
13
14export const YARROW_MODEL = Object.freeze({
15  id: 'equiprobable-remainders-v1',
16  description: '依《筮仪》的四种余数等可能建模,再在对应余数的合法分堆中等概率选取一次;并非所有分堆大小等概率。',
17  probabilities: Object.freeze({ 6: 1 / 16, 7: 5 / 16, 8: 7 / 16, 9: 3 / 16 }),
18});
19
20function drawChecked(drawInt, bound) {
21  const value = drawInt(bound);
22  if (!Number.isInteger(value) || value < 0 || value >= bound) {
23    throw new RangeError(`随机源必须返回 0 至 ${bound - 1} 之间的整数。`);
24  }
25  return value;
26}
27
28// A pile divisible by four leaves FOUR stalks, never zero.
29function remainder(pile) {
30  return ((pile - 1) % 4) + 1;
31}
32
33/** Create one line: three changes starting from 49 stalks. */
34export function createLine(drawInt) {
35  if (typeof drawInt !== 'function') {
36    throw new TypeError('请提供 drawInt(maxExclusive) 随机整数函数。');
37  }
38  let before = 49;
39  const steps = [];
40
41  for (let change = 0; change < 3; change += 1) {
42    const leftRemainder = drawChecked(drawInt, 4) + 1;
43    // Both piles must be nonempty after hanging one from the right.
44    const splitCount = Math.floor((before - 2 - leftRemainder) / 4) + 1;
45    const left = leftRemainder + 4 * drawChecked(drawInt, splitCount);
46    const right = before - left;
47    const hanging = 1;
48    const rightRemainder = remainder(right - hanging);
49    const removed = hanging + leftRemainder + rightRemainder;
50    const remaining = before - removed;
51
52    steps.push({
53      before,
54      left,
55      right,
56      hanging,
57      leftRemainder,
58      rightRemainder,
59      removed,
60      remaining,
61    });
62    before = remaining;
63  }
64
65  return { value: before / 4, steps };
66}
67
68/** Six lines in construction order: bottom (初爻) to top (上爻). */
69export function createCast(drawInt) {
70  return { lines: Array.from({ length: 6 }, () => createLine(drawInt)) };
71}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.