Skip to content

Commit 420d82e

Browse files
committed
fix(orthogonal-demo): 将K上限从N²调整为N³并优化图表坐标轴
- K上限改为N³以扩展多项式增长范围 - 横轴从log轴改为value轴,手动计算log10避免ECharts刻度稀疏问题 - 调整滑杆布局和输入框宽度以适应更大的K值范围 - 修复tooltip中K值还原显示,隐藏右端刻度标签防止截断
1 parent 5e1161d commit 420d82e

4 files changed

Lines changed: 57 additions & 20 deletions

File tree

web-tools/orthogonal-demo/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ JMLR 14 (2013), arXiv:1306.0256。
2626
是两者的渐近桥梁**——拖动 K 滑杆可看到黄线逐渐贴合蓝线,直观演示极限定理的收敛。
2727
细节见 §5。
2828

29-
K 的调节上限取 $N^2$(多项式增长,$\ln K/N = 2\ln N/N$ 全程较小,保证大部分范围
29+
K 的调节上限取 $N^3$(多项式增长,$\ln K/N = 3\ln N/N$ 全程较小,保证大部分范围
3030
处于次指数区域)。界面提供两个区域指示:中位数曲线在 $\ln K/N > 0.05$ 的区间铺
3131
灰色背景;统计栏显示当前 $\ln K/N$ 数值与所属区域
3232
(< 0.05 次指数区域 / 0.05~0.25 过渡区 / > 0.25 超出定理覆盖范围)。

web-tools/orthogonal-demo/css/style.css

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,18 +93,36 @@ header h1 {
9393
white-space: nowrap;
9494
}
9595

96-
.control input[type="range"] {
96+
/* K 滑杆组:basis 较大,宽屏下挤不开时与 MC 控制组分行,
97+
滑杆随窗口宽度弹性伸长(K 跨 ~12 个数量级,需要精细定位) */
98+
.control-slider {
99+
flex: 1 1 460px;
100+
}
101+
102+
.control-slider input[type="range"] {
103+
flex: 1;
104+
width: auto;
105+
min-width: 200px;
106+
}
107+
108+
/* N 只有 64~8192(约 2 个数量级),滑杆固定小宽度即可 */
109+
#sliderN {
97110
width: 220px;
98111
}
99112

100113
.control input[type="number"] {
101-
width: 90px;
114+
width: 64px;
102115
padding: 4px 6px;
103116
border: 1px solid #d0d7de;
104117
border-radius: 6px;
105118
font-size: 14px;
106119
}
107120

121+
/* K 上限 N³(8192³ ≈ 5.5e11,12 位数字),输入框需要更宽 */
122+
#inputK {
123+
width: 130px;
124+
}
125+
108126
.control-mc {
109127
flex-wrap: wrap;
110128
}

web-tools/orthogonal-demo/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ <h1>高维随机向量:最小夹角 / 最大点乘</h1>
2727
<input id="sliderN" type="range" min="0" max="1000" value="500" />
2828
<input id="inputN" type="number" min="64" max="8192" step="1" />
2929
</div>
30-
<div class="control">
30+
<div class="control control-slider">
3131
<label for="sliderK">向量数 K</label>
3232
<input id="sliderK" type="range" min="0" max="1000" value="500" />
3333
<input id="inputK" type="number" min="2" step="1" />
@@ -77,7 +77,7 @@ <h1>高维随机向量:最小夹角 / 最大点乘</h1>
7777
<div class="chart-wrap">
7878
<div id="chart1" class="chart"></div>
7979
<p class="chart-note">
80-
横轴 K(对数刻度,上限 N²),纵轴为 max ρ 的中位数(<strong class="legend-note note-orange">一阶近似 √(2·lnM/N)(双侧 √(2·ln(2M)/N))
80+
横轴 K(对数刻度,上限 N³),纵轴为 max ρ 的中位数(<strong class="legend-note note-orange">一阶近似 √(2·lnM/N)(双侧 √(2·ln(2M)/N))
8181
为"期望超标次数=1"的启发式,口径最接近众数,仅作量级对照</strong>)。<strong class="legend-note note-blue">蓝实线 F^M 全 K 适用</strong><strong class="legend-note note-yellow">黄虚线 Gumbel 为渐近对照</strong>;竖线为当前 K;
8282
灰色区域表示 lnK/N &gt; 0.05,理论线开始偏离。
8383
</p>

web-tools/orthogonal-demo/js/app.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
const N_MIN = 64;
1313
const N_MAX = 8192;
1414

15-
// K 上限:N²(多项式增长,lnK/N = 2lnN/N 全程保持在次指数区域)
15+
// K 上限:N³(多项式增长,lnK/N = 3lnN/N 全程保持在次指数区域)
1616
function kMax(N) {
17-
return N * N;
17+
return N * N * N;
1818
}
1919
// 渐近区域阈值:lnK/N 小于 SUB_EXP 视为次指数区域(理论线可信),
2020
// 大于 TRANSITION 视为超出定理覆盖范围
@@ -554,14 +554,24 @@
554554
// 右侧角度副轴的范围据此换算
555555
const yMax = 1.05 * T.firstOrderMean(N, kHi, state.twoSided);
556556

557+
// ECharts log 轴在跨度 ~12 个数量级时只生成个位数刻度,且
558+
// customValues / interval / splitNumber 对 log 轴均无效(5.5.0 实测);
559+
// 改用 value 轴 + 数据 x 手动取 log10(K),刻度间隔 interval:1(每个数量级一个)
560+
const lg = Math.log10;
561+
const logData = (data) => data.map((p) => [lg(p[0]), p[1]]);
562+
557563
// lnK/N > SUB_EXP 的区间铺浅灰背景:此区域理论线开始偏离
558564
const kThreshold = Math.exp(SUB_EXP * N);
559565
const markAreaData =
560566
kThreshold < kHi
561-
? [[{ xAxis: kThreshold }, { xAxis: kHi }]]
567+
? [[{ xAxis: lg(kThreshold) }, { xAxis: lg(kHi) }]]
562568
: [];
563569

564570
const mcOv = mcOverlay1(td.ks);
571+
// MC 叠加层数据同样是 [K, ρ],统一做 log10 变换
572+
const mcSeries = mcOv.series.map((s) =>
573+
Object.assign({}, s, { data: logData(s.data) })
574+
);
565575

566576
chart1.setOption(
567577
{
@@ -571,10 +581,11 @@
571581
tooltip: {
572582
trigger: 'axis',
573583
// 理论与模拟系列共享同一整数 K 网格(theoryData1.ks),
574-
// axis 触发下同 K 全部显示;IQR 带是闭合多边形(每个 K 两个点),不进 tip
584+
// axis 触发下同 K 全部显示;IQR 带是闭合多边形(每个 K 两个点),不进 tip。
585+
// 横轴是 log10(K),显示时还原为 K(round 吸收浮点尾数)
575586
formatter: (params) => {
576587
let html =
577-
'K = ' + Math.round(params[0].axisValue).toLocaleString('en-US');
588+
'K = ' + Math.round(Math.pow(10, Number(params[0].axisValue))).toLocaleString('en-US');
578589
for (const p of params) {
579590
if (p.seriesName.indexOf('IQR') >= 0) continue;
580591
const v = p.value[1];
@@ -589,17 +600,25 @@
589600
legend: { bottom: 0 },
590601
grid: { left: 60, right: 40, top: 30, bottom: 45 },
591602
xAxis: {
592-
type: 'log',
603+
// 名义上是对数刻度:坐标值 = log10(K)(见上面 lg/logData 变换)
604+
type: 'value',
593605
name: 'K(向量个数)',
594606
// 轴名在刻度数值行之下、右对齐收于轴端内侧:
595607
// nameGap 对 end 放置不生效,用 verticalAlign top + padding 下移;
596608
// padding 右侧留白与右轴 90° 标签隔开
597609
nameTextStyle: { align: 'right', verticalAlign: 'top', padding: [24, 0, 0, 0] },
598-
min: 2,
599-
max: kHi,
600-
// K 是整数计数;log 轴末端刻度由 10^log10(N²) 反算,带浮点尾数,需取整
610+
// min 取 log 空间整数(K=1):value 轴 interval 网格从 min 起对齐,
611+
// 这样 interval:1 的刻度恰好落在每个数量级上(数据从 K=2 起,
612+
// 左端空出 0.3 个数量级可忽略)
613+
min: 0,
614+
max: lg(kHi),
615+
// 每个数量级一个刻度;刻度值为整数 log10(K),标签还原为 K 的整数码
616+
interval: 1,
601617
axisLabel: {
602-
formatter: (v) => Math.round(v).toLocaleString('en-US'),
618+
formatter: (v) => Math.round(Math.pow(10, v)).toLocaleString('en-US'),
619+
// 右端点(K 上限)标签贴轴缘会被截断,且位数随 N 变化,直接隐藏;
620+
// 上限可在控件输入框看到
621+
showMaxLabel: false,
603622
},
604623
},
605624
yAxis: [
@@ -626,7 +645,7 @@
626645
type: 'line',
627646
showSymbol: false,
628647
smooth: true,
629-
data: td.firstOrder,
648+
data: logData(td.firstOrder),
630649
markArea: {
631650
silent: true,
632651
itemStyle: { color: 'rgba(0,0,0,0.045)' },
@@ -645,13 +664,13 @@
645664
type: 'line',
646665
showSymbol: false,
647666
smooth: true,
648-
data: td.betaMedian,
667+
data: logData(td.betaMedian),
649668
markLine: {
650669
silent: true,
651670
symbol: 'none',
652671
lineStyle: { type: 'dashed', color: '#999' },
653672
label: { formatter: 'K = ' + K, position: 'insideEndTop' },
654-
data: [{ xAxis: K }],
673+
data: [{ xAxis: lg(K) }],
655674
},
656675
},
657676
{
@@ -660,9 +679,9 @@
660679
showSymbol: false,
661680
smooth: true,
662681
lineStyle: { type: 'dashed' },
663-
data: td.gumbelMedian,
682+
data: logData(td.gumbelMedian),
664683
},
665-
].concat(mcOv.series),
684+
].concat(mcSeries),
666685
},
667686
{ notMerge: true }
668687
);

0 commit comments

Comments
 (0)