|
47 | 47 | // 面板二 |
48 | 48 | sliderD: $('sliderD'), |
49 | 49 | inputD: $('inputD'), |
| 50 | + hControl: $('hControl'), |
| 51 | + sliderH: $('sliderH'), |
| 52 | + inputH: $('inputH'), |
50 | 53 | inputSigmaSum: $('inputSigmaSum'), |
51 | 54 | btnSampleSum: $('btnSampleSum'), |
52 | 55 | sumStats: $('sumStats'), |
|
60 | 63 | useSeed: true, |
61 | 64 | seed: 42, |
62 | 65 | elem: { sigma2: 1, N: 200000, op: 'product' }, |
63 | | - sum: { D: 16, sigma2preset: '1/D', sigma2: 1 / 16, mode: 'dotRandom' }, |
| 66 | + sum: { D: 16, H: 64, sigma2preset: '1/D', sigma2: 1 / 16, mode: 'dotRandom' }, |
64 | 67 | }; |
65 | 68 |
|
66 | 69 | var pairs = null; // 面板一样本对 {x, y};null = 未采样/已失效 |
67 | 70 | var sumCache = {}; // 面板二缓存:modeId -> { samples, M };参数变更即清空 |
| 71 | + var projW = null; // projDot 的固定投影矩阵 {D, H, key, wQ, wK};随参数/种子失效 |
68 | 72 | var charts = {}; |
69 | 73 |
|
70 | 74 | /** 随机种子(全局共享);salt 区分各条随机流,保证两面板样本无交集 */ |
|
226 | 230 | }); |
227 | 231 | } |
228 | 232 |
|
| 233 | + /** |
| 234 | + * projDot 的投影矩阵:σ_w² = 1/D(标准初始化),由种子生成后固定; |
| 235 | + * D、H 或种子变化时重建。 |
| 236 | + */ |
| 237 | + function getProjW() { |
| 238 | + var D = state.sum.D; |
| 239 | + var H = state.sum.H; |
| 240 | + var key = D + '|' + H + '|' + currentSeed(0); |
| 241 | + if (projW && projW.key === key) return projW; |
| 242 | + var g = S.makeRng(currentSeed(41)); |
| 243 | + var sigmaW = 1 / Math.sqrt(D); |
| 244 | + projW = { |
| 245 | + key: key, |
| 246 | + wQ: S.makeProjection(g, H, D, sigmaW), |
| 247 | + wK: S.makeProjection(g, H, D, sigmaW), |
| 248 | + }; |
| 249 | + return projW; |
| 250 | + } |
| 251 | + |
229 | 252 | /** 采指定模式并存入缓存;M 随 D 自适应(总采样量封顶 ~1.6e7 个分量) */ |
230 | 253 | function sampleSumMode(modeId) { |
| 254 | + if (modeId === 'projDot') { |
| 255 | + var D = state.sum.D; |
| 256 | + var H = state.sum.H; |
| 257 | + var sigma = Math.sqrt(state.sum.sigma2); |
| 258 | + var w = getProjW(); |
| 259 | + // 每样本 2HD 次乘加:预算 ~3e8,M 随之自适应 |
| 260 | + var M = Math.max(500, Math.min(20000, Math.floor(3e8 / (2 * H * D)))); |
| 261 | + var samples = S.sampleProjDot(S.makeRng(currentSeed(43)), M, D, H, sigma, w.wQ, w.wK); |
| 262 | + sumCache.projDot = { samples: samples, M: M }; |
| 263 | + return; |
| 264 | + } |
231 | 265 | var D = state.sum.D; |
232 | 266 | var sigma = Math.sqrt(state.sum.sigma2); |
233 | 267 | var M = Math.max(2000, Math.min(60000, Math.floor(1.6e7 / (2 * D)))); |
|
245 | 279 | function renderSum() { |
246 | 280 | var sigma = Math.sqrt(state.sum.sigma2); |
247 | 281 | var D = state.sum.D; |
| 282 | + var H = state.sum.H; // 仅 projDot 模式使用 |
248 | 283 | var mode = T.SUM_MODES.filter(function (m) { |
249 | 284 | return m.id === state.sum.mode; |
250 | 285 | })[0]; |
251 | | - var r = mode.range(D, sigma); |
| 286 | + var r = mode.range(D, sigma, H); |
252 | 287 | var lo = r[0]; |
253 | 288 | var hi = r[1]; |
254 | 289 |
|
|
263 | 298 | animation: false, |
264 | 299 | data: theoryLine( |
265 | 300 | function (z) { |
266 | | - return mode.pdf(z, D, sigma); |
| 301 | + return mode.pdf(z, D, sigma, H); |
267 | 302 | }, |
268 | 303 | lo, |
269 | 304 | hi, |
|
318 | 353 | var text = |
319 | 354 | 'D=' + |
320 | 355 | D + |
| 356 | + (mode.id === 'projDot' ? '、H=' + H : '') + |
321 | 357 | '、σ²=' + |
322 | 358 | fmt(state.sum.sigma2) + |
323 | 359 | ':<b>' + |
324 | 360 | mode.label + |
325 | 361 | '</b> 理论 均值 ' + |
326 | | - fmt(mode.mean(D, sigma)) + |
| 362 | + fmt(mode.mean(D, sigma, H)) + |
327 | 363 | '、方差 ' + |
328 | | - fmt(mode.variance(D, sigma)); |
| 364 | + fmt(mode.variance(D, sigma, H)); |
329 | 365 | if (pack) { |
330 | 366 | var mv = S.sampleMeanVar(pack.samples); |
331 | 367 | text += |
|
342 | 378 | el.sumStats.innerHTML = text; |
343 | 379 | } |
344 | 380 |
|
| 381 | + /** H 控件只在投影点积模式下显示 */ |
| 382 | + function refreshHVisibility() { |
| 383 | + el.hControl.style.display = state.sum.mode === 'projDot' ? '' : 'none'; |
| 384 | + } |
| 385 | + |
345 | 386 | // ---------- 样本失效 ---------- |
346 | 387 | function invalidateElement() { |
347 | 388 | pairs = null; |
|
351 | 392 |
|
352 | 393 | function invalidateSum() { |
353 | 394 | sumCache = {}; |
| 395 | + projW = null; |
354 | 396 | markNeedSample(el.btnSampleSum, true); |
355 | 397 | renderSum(); |
356 | 398 | } |
|
433 | 475 | refreshSigma2(); |
434 | 476 | invalidateSum(); |
435 | 477 | }); |
| 478 | + // H:滑块走 2 的幂,输入框允许任意 1~512 |
| 479 | + function onHChange(H) { |
| 480 | + state.sum.H = H; |
| 481 | + invalidateSum(); |
| 482 | + } |
| 483 | + el.sliderH.addEventListener('input', function () { |
| 484 | + var H = Math.pow(2, +el.sliderH.value); |
| 485 | + el.inputH.value = H; |
| 486 | + onHChange(H); |
| 487 | + }); |
| 488 | + el.inputH.addEventListener('change', function () { |
| 489 | + var H = Math.round(+el.inputH.value); |
| 490 | + if (!isFinite(H)) { |
| 491 | + el.inputH.value = state.sum.H; |
| 492 | + return; |
| 493 | + } |
| 494 | + H = Math.max(1, Math.min(512, H)); |
| 495 | + el.inputH.value = H; |
| 496 | + el.sliderH.value = Math.max(0, Math.min(9, Math.round(Math.log2(H)))); |
| 497 | + onHChange(H); |
| 498 | + }); |
436 | 499 | document.querySelectorAll('input[name="sumMode"]').forEach(function (r) { |
437 | 500 | r.addEventListener('change', function () { |
438 | 501 | state.sum.mode = r.value; |
| 502 | + refreshHVisibility(); |
439 | 503 | renderSum(); // 切换模式不失效:有缓存则显示直方图,无则纯理论线 |
440 | 504 | }); |
441 | 505 | }); |
|
468 | 532 | charts.sum = echarts.init($('chartSum')); |
469 | 533 | bindControls(); |
470 | 534 | refreshSigma2(); |
| 535 | + refreshHVisibility(); |
471 | 536 | markNeedSample(el.btnSampleElem, true); |
472 | 537 | markNeedSample(el.btnSampleSum, true); |
473 | 538 | renderElement(); |
|
0 commit comments