Files

375 lines
44 KiB
HTML
Raw Permalink Normal View History

2026-07-17 07:39:45 +00:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>特别号码预测系统</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: #0f1923; color: #e0e6ed; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { text-align: center; color: #4fc3f7; margin-bottom: 8px; font-size: 24px; }
.subtitle { text-align: center; color: #78909c; margin-bottom: 24px; font-size: 13px; }
.input-section { background: #1a2634; border-radius: 12px; padding: 24px; margin-bottom: 20px; }
.input-section h2 { color: #4fc3f7; font-size: 16px; margin-bottom: 16px; }
.input-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.input-group { display: flex; flex-direction: column; gap: 4px; }
.input-group label { font-size: 12px; color: #78909c; }
input[type="number"], input[type="text"] {
background: #0f1923; border: 1px solid #37474f; border-radius: 6px;
color: #e0e6ed; padding: 8px 12px; font-size: 16px; width: 60px; text-align: center;
}
input[type="number"]:focus, input[type="text"]:focus { outline: none; border-color: #4fc3f7; }
.paste-input { width: 100% !important; margin-top: 12px; }
.btn {
background: linear-gradient(135deg, #4fc3f7, #0288d1); border: none; border-radius: 8px;
color: white; padding: 10px 32px; font-size: 16px; cursor: pointer; font-weight: 600;
transition: transform 0.1s;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.optional-section { margin-top: 16px; padding-top: 16px; border-top: 1px solid #37474f; }
.optional-section summary { cursor: pointer; color: #78909c; font-size: 13px; }
.optional-section textarea {
width: 100%; background: #0f1923; border: 1px solid #37474f; border-radius: 6px;
color: #e0e6ed; padding: 8px; font-size: 14px; margin-top: 8px; min-height: 60px; resize: vertical;
}
.results-section { background: #1a2634; border-radius: 12px; padding: 24px; margin-top: 20px; }
.results-section h2 { color: #4fc3f7; font-size: 16px; margin-bottom: 16px; }
.summary-box { display: flex; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.summary-card { background: #0f1923; border-radius: 8px; padding: 12px 20px; min-width: 120px; }
.summary-card .label { font-size: 11px; color: #78909c; }
.summary-card .value { font-size: 20px; font-weight: 700; color: #4fc3f7; }
.summary-card .value.green { color: #66bb6a; }
.summary-card .value.orange { color: #ffa726; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; padding: 8px 12px; color: #78909c; border-bottom: 1px solid #37474f; font-size: 12px; }
td { padding: 6px 12px; border-bottom: 1px solid #1e2d3a; }
tr:hover { background: #1e2d3a; }
.rank { font-weight: 700; }
.num-ball { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; font-weight: 700; font-size: 14px; }
.num-ball.top1 { background: #e91e63; color: white; box-shadow: 0 0 8px rgba(233,30,99,0.5); }
.num-ball.top3 { background: #ff5722; color: white; }
.num-ball.top5 { background: #ff9800; color: white; }
.num-ball.top10 { background: #4caf50; color: white; }
.num-ball.normal { background: #37474f; color: #b0bec5; }
.num-ball.excluded { background: #1a2634; color: #546e7a; border: 1px dashed #37474f; }
.score { font-weight: 700; }
.score.high { color: #66bb6a; }
.score.mid { color: #ffa726; }
.score.low { color: #78909c; }
.score.negative { color: #ef5350; }
.score.excluded { color: #546e7a; }
.zone-badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
.zone-1 { background: #1b5e20; color: #a5d6a7; }
.zone-2 { background: #0d47a1; color: #90caf9; }
.zone-3 { background: #4a148c; color: #ce93d8; }
.zone-4 { background: #e65100; color: #ffcc80; }
.zone-5 { background: #b71c1c; color: #ef9a9a; }
.dir-yes { color: #66bb6a; }
.dir-no { color: #ef5350; }
.reasons { color: #78909c; font-size: 12px; }
.section-divider td { border-top: 2px dashed #37474f; }
.section-divider .divider-label { color: #546e7a; font-size: 12px; font-style: italic; padding-top: 12px; }
.error { color: #ef5350; font-size: 14px; margin-top: 8px; }
.hidden { display: none; }
.debug { color: #546e7a; font-size: 11px; margin-top: 8px; }
</style>
</head>
<body>
<div class="container">
<h1>特别号码预测系统</h1>
<p class="subtitle">橡皮筋效应 + 欠账效应 + 主号邻近性 + 间隙效应 | 回测Top1命中率约8% Top5约34% Top10约62%</p>
<div class="input-section">
<h2>输入上一期数据</h2>
<div class="input-row">
<div class="input-group"><label>主号1</label><input type="number" id="m1" min="1" max="49" placeholder="3"></div>
<div class="input-group"><label>主号2</label><input type="number" id="m2" min="1" max="49" placeholder="10"></div>
<div class="input-group"><label>主号3</label><input type="number" id="m3" min="1" max="49" placeholder="19"></div>
<div class="input-group"><label>主号4</label><input type="number" id="m4" min="1" max="49" placeholder="24"></div>
<div class="input-group"><label>主号5</label><input type="number" id="m5" min="1" max="49" placeholder="37"></div>
<div class="input-group"><label>主号6</label><input type="number" id="m6" min="1" max="49" placeholder="41"></div>
<div class="input-group"><label>特别号</label><input type="number" id="sp" min="1" max="49" placeholder="39" style="border-color:#e91e63;"></div>
<button class="btn" onclick="predict()">预测</button>
</div>
<input type="text" class="paste-input" id="paste" placeholder="或粘贴格式: 03-10-19-24-37-41,39" oninput="parsePaste()">
<details class="optional-section">
<summary>高级选项: 输入最近30期特别号 (用于欠账计算,逗号分隔)</summary>
<textarea id="last30" placeholder="例如: 25,9,26,25,39,13,48,27,9,31,..."></textarea>
</details>
<div id="error" class="error hidden"></div>
<div id="debug" class="debug hidden"></div>
</div>
<div id="results" class="results-section hidden">
<h2>完整排名 (43个候选号)</h2>
<div class="summary-box" id="summaryBox"></div>
<table>
<thead>
<tr><th>排名</th><th>号码</th><th></th><th>方向</th><th>间隙</th><th>得分</th><th>理由</th></tr>
</thead>
<tbody id="resultBody"></tbody>
</table>
</div>
</div>
<script>
const ASSOC_DATA = {"1":{"1":0.0119,"2":0.0198,"3":0.0357,"4":0.0437,"5":0.0238,"6":0.0317,"7":0.0119,"8":0.0238,"9":0.0238,"10":0.0159,"11":0.0159,"12":0.0317,"13":0.0238,"14":0.0198,"15":0.0119,"16":0.0119,"17":0.0119,"18":0.0238,"19":0.0397,"20":0.0159,"21":0.0317,"22":0.0198,"23":0.0278,"24":0.0119,"25":0.0238,"26":0.0119,"27":0.0238,"28":0.0159,"29":0.0119,"30":0.0238,"31":0.0278,"33":0.0278,"34":0.0119,"36":0.0079,"37":0.0198,"38":0.0198,"39":0.0397,"40":0.0079,"41":0.0317,"42":0.0238,"43":0.0159,"44":0.0397,"45":0.0079,"46":0.0119,"47":0.0278,"48":0.0119,"49":0.0159},"2":{"1":0.0191,"2":0.0344,"3":0.0191,"4":0.0153,"5":0.0191,"6":0.0153,"7":0.0191,"8":0.0267,"9":0.0229,"10":0.0305,"11":0.0115,"12":0.0191,"13":0.0115,"14":0.0382,"15":0.0153,"16":0.0153,"17":0.0229,"18":0.0229,"19":0.0229,"20":0.0229,"21":0.0229,"22":0.0153,"23":0.0305,"24":0.0115,"25":0.0305,"26":0.0191,"27":0.0153,"28":0.0153,"29":0.0153,"30":0.0191,"31":0.0153,"32":0.0115,"33":0.0153,"34":0.0115,"35":0.0153,"36":0.0038,"37":0.0229,"38":0.0267,"39":0.0267,"40":0.0305,"41":0.0153,"42":0.0267,"43":0.0191,"44":0.0153,"45":0.0267,"46":0.0382,"47":0.0115,"48":0.0267,"49":0.0229},"3":{"1":0.0296,"2":0.0111,"3":0.0148,"4":0.0259,"5":0.0148,"6":0.0333,"7":0.0148,"8":0.0259,"9":0.0111,"10":0.0259,"11":0.0037,"12":0.037,"13":0.0185,"14":0.0185,"15":0.0259,"16":0.0148,"17":0.0222,"18":0.0037,"19":0.0296,"20":0.0222,"21":0.0259,"22":0.0111,"23":0.0185,"24":0.0074,"25":0.0111,"26":0.0185,"27":0.0148,"28":0.0259,"29":0.0222,"30":0.0222,"31":0.0185,"32":0.0259,"33":0.0148,"34":0.0222,"35":0.0259,"36":0.0296,"37":0.0111,"38":0.0222,"39":0.0333,"40":0.0296,"41":0.0222,"42":0.0148,"43":0.0185,"44":0.0222,"45":0.0259,"46":0.0185,"47":0.0259,"48":0.0148,"49":0.0222},"4":{"1":0.0215,"2":0.0215,"3":0.03,"4":0.0172,"5":0.0129,"6":0.0258,"7":0.0215,"8":0.0215,"9":0.0386,"10":0.0258,"11":0.0129,"12":0.0172,"13":0.0129,"14":0.03,"15":0.0129,"16":0.0129,"17":0.0258,"18":0.0129,"19":0.0215,"20":0.0172,"21":0.0129,"22":0.0215,"23":0.0043,"24":0.0172,"25":0.0129,"26":0.0215,"27":0.0172,"28":0.0129,"29":0.0086,"30":0.0129,"31":0.0215,"32":0.0258,"33":0.03,"34":0.0172,"35":0.0258,"36":0.0172,"37":0.03,"38":0.0258,"39":0.03,"40":0.03,"41":0.0215,"42":0.0258,"43":0.0215,"44":0.0086,"45":0.0343,"46":0.0258,"47":0.0172,"48":0.0172,"49":0.0215},"5":{"1":0.0194,"2":0.0155,"3":0.0116,"4":0.0271,"5":0.0349,"6":0.0388,"7":0.0155,"8":0.0116,"9":0.0271,"10":0.031,"11":0.0271,"12":0.0155,"13":0.0194,"14":0.0194,"15":0.0155,"16":0.0233,"17":0.0116,"18":0.0271,"19":0.0349,"20":0.0155,"21":0.0349,"22":0.0155,"23":0.0194,"24":0.0194,"25":0.0155,"26":0.0116,"27":0.0078,"28":0.031,"29":0.0116,"30":0.0194,"31":0.0233,"32":0.0233,"33":0.0155,"34":0.0039,"35":0.0233,"36":0.0078,"37":0.0155,"38":0.0078,"39":0.031,"40":0.0233,"41":0.0233,"42":0.0155,"43":0.031,"44":0.0155,"45":0.0233,"46":0.0194,"47":0.0116,"48":0.0155,"49":0.0426},"6":{"1":0.0129,"2":0.0129,"3":0.0258,"4":0.0043,"5":0.0086,"6":0.0258,"7":0.0043,"8":0.0172,"9":0.0472,"10":0.0215,"11":0.0215,"12":0.0086,"13":0.0086,"14":0.0258,"15":0.03,"16":0.0215,"17":0.0086,"18":0.0172,"19":0.03,"20":0.0129,"21":0.0172,"22":0.0043,"23":0.03,"24":0.0429,"25":0.0086,"26":0.0258,"27":0.0215,"28":0.0258,"29":0.0215,"30":0.0215,"31":0.0172,"32":0.0258,"33":0.0343,"34":0.0043,"35":0.0086,"36":0.0172,"37":0.03,"38":0.0172,"39":0.0343,"40":0.0258,"41":0.0472,"42":0.0129,"43":0.0215,"44":0.0043,"45":0.0258,"46":0.0172,"47":0.0258,"48":0.0215,"49":0.0258},"7":{"1":0.032,"2":0.016,"3":0.012,"4":0.016,"5":0.016,"6":0.032,"7":0.016,"8":0.024,"9":0.016,"10":0.04,"11":0.032,"12":0.016,"13":0.024,"14":0.032,"15":0.02,"16":0.028,"17":0.008,"18":0.024,"19":0.028,"20":0.028,"21":0.028,"22":0.024,"23":0.024,"24":0.012,"25":0.02,"26":0.016,"27":0.028,"28":0.016,"29":0.028,"30":0.016,"31":0.008,"32":0.016,"33":0.008,"34":0.012,"35":0.012,"36":0.016,"37":0.024,"38":0.016,"39":0.008,"40":0.012,"41":0.016,"42":0.016,"43":0.02,"44":0.024,"45":0.024,"46":0.032,"47":0.012,"48":0.02,"49":0.032},"8":{"1":0.017,"2":0.0213,"3":0.0298,"4":0.0213,"5":0.0213,"6":0.0213,"7":0.0255,"8
function getZone(v) {
if (v <= 10) return 1;
else if (v <= 20) return 2;
else if (v <= 30) return 3;
else if (v <= 40) return 4;
else return 5;
}
function parsePaste() {
const raw = document.getElementById('paste').value.trim();
if (!raw) return;
const parts = raw.split(',');
if (parts.length >= 2) {
const mains = parts[0].split('-').map(x => parseInt(x.trim()));
const sp = parseInt(parts[1].trim());
if (mains.length === 6 && mains.every(m => !isNaN(m) && m >= 1 && m <= 49) && !isNaN(sp)) {
for (let i = 0; i < 6; i++) document.getElementById('m' + (i+1)).value = mains[i];
document.getElementById('sp').value = sp;
}
}
}
function predict() {
const errorEl = document.getElementById('error');
const debugEl = document.getElementById('debug');
errorEl.classList.add('hidden');
debugEl.classList.add('hidden');
const mains = [];
for (let i = 1; i <= 6; i++) {
const v = parseInt(document.getElementById('m' + i).value);
if (isNaN(v) || v < 1 || v > 49) {
errorEl.textContent = '请输入有效的6个主号(1-49)';
errorEl.classList.remove('hidden');
return;
}
mains.push(v);
}
const sp = parseInt(document.getElementById('sp').value);
if (isNaN(sp) || sp < 1 || sp > 49) {
errorEl.textContent = '请输入有效的特别号(1-49)';
errorEl.classList.remove('hidden');
return;
}
if (mains.includes(sp)) {
errorEl.textContent = '特别号不能与主号重复';
errorEl.classList.remove('hidden');
return;
}
const uniqueMains = new Set(mains);
if (uniqueMains.size !== 6) {
errorEl.textContent = '6个主号不能有重复';
errorEl.classList.remove('hidden');
return;
}
const last30Raw = document.getElementById('last30').value.trim();
let last30 = [];
if (last30Raw) {
last30 = last30Raw.split(/[,\s]+/).map(x => parseInt(x.trim())).filter(x => !isNaN(x));
}
const zone = getZone(sp);
let targetLo, targetHi, dirLabel;
if (zone === 1) { targetLo = 21; targetHi = 49; dirLabel = '上跳'; }
else if (zone === 2) { targetLo = 21; targetHi = 49; dirLabel = '上跳'; }
else if (zone === 3) { targetLo = 1; targetHi = 49; dirLabel = '无方向'; }
else if (zone === 4) { targetLo = 1; targetHi = 30; dirLabel = '下跳'; }
else { targetLo = 1; targetHi = 30; dirLabel = '下跳'; }
const sorted = [...mains].sort((a, b) => a - b);
const gaps = [];
for (let i = 0; i < 5; i++) {
gaps.push({ size: sorted[i+1] - sorted[i] - 1, lo: sorted[i], hi: sorted[i+1] });
}
// 收集全部43个候选号(排除6个主号)
const allResults = [];
for (let c = 1; c <= 49; c++) {
// 注: 不排除上一期主号, 因为下期特别号可以与上期主号重复
const inDir = (c >= targetLo && c <= targetHi);
const inGap = gaps.find(g => g.lo < c && c < g.hi);
const inBigGap = inGap && inGap.size >= 6;
let score = 0;
let reasons = [];
let status = 'excluded'; // excluded | candidate
// 方向
if (!inDir) {
score = -10;
reasons.push('方向不符');
} else {
reasons.push(dirLabel + '方向符合');
// 间隙
if (!inGap) {
if (c < sorted[0]) reasons.push('低于所有主号');
else if (c > sorted[5]) reasons.push('高于所有主号');
else reasons.push('不在间隙内');
} else if (inGap.size < 6) {
reasons.push('小间隙(g=' + inGap.size + ')');
} else {
// 在大间隙内, 是候选
status = 'candidate';
if (inGap.size >= 10) {
score += 2;
reasons.push('大间隙(' + inGap.lo + '-' + inGap.hi + ',g=' + inGap.size + ')(+2)');
} else {
score += 1;
reasons.push('中间隙(' + inGap.lo + '-' + inGap.hi + ',g=' + inGap.size + ')(+1)');
}
// 欠账
if (last30.length >= 10) {
if (!last30.includes(c)) {
score += 2;
reasons.push('欠账(+2)');
} else {
reasons.push('近期出现过');
}
}
// 主号±5
let minDist = 99;
for (const m of mains) {
const d = Math.abs(c - m);
if (d < minDist) minDist = d;
}
if (minDist <= 5) {
score += 1;
reasons.push('主号±5(距' + minDist + ')(+1)');
} else {
reasons.push('距主号' + minDist);
}
// 信号4: 主号关联 (上期主号X -> 下期特别号X的统计概率)
let assocScore = 0;
let assocDetails = [];
for (const m of mains) {
if (ASSOC_DATA[m] && ASSOC_DATA[m][c]) {
const prob = ASSOC_DATA[m][c];
assocScore += prob;
if (prob >= 0.04) {
assocDetails.push('主' + m + '->特' + c + '(' + (prob*100).toFixed(1) + '%)');
}
}
}
score += assocScore * 50;
if (assocScore > 0.05) {
reasons.push('主号关联(' + (assocScore*100).toFixed(0) + '%)(+' + (assocScore*50).toFixed(1) + ')');
}
}
}
let gapInfo = '-';
if (inGap) gapInfo = inGap.lo + '-' + inGap.hi + '(g=' + inGap.size + ')';
allResults.push({
number: c, score: score, zone: getZone(c),
inDir: inDir, inBigGap: inBigGap, status: status,
reasons: reasons.join(' | '), gapInfo: gapInfo
});
}
// 排序: 候选在前(分数降序+号码升序), 排除在后
const candidates = allResults.filter(r => r.status === 'candidate');
const excluded = allResults.filter(r => r.status === 'excluded');
candidates.sort((a, b) => b.score - a.score || a.number - b.number);
excluded.sort((a, b) => b.score - a.score || a.number - b.number);
const results = [...candidates, ...excluded];
// debug信息
debugEl.textContent = '调试: 总候选' + allResults.length + '个, 进池' + candidates.length + '个, 排除' + excluded.length + '个';
debugEl.classList.remove('hidden');
renderResults(results, zone, dirLabel, sp, mains, candidates.length);
}
function renderResults(results, zone, dirLabel, sp, mains, candCount) {
document.getElementById('results').classList.remove('hidden');
// Top只从候选池取, 不含排除的号
const top1 = results[0] || { number: '-', score: 0 };
const top3 = results.slice(0, 3).map(r => r.number);
const top5 = results.slice(0, 5).map(r => r.number);
const top10 = results.slice(0, 10).map(r => r.number);
const summaryBox = document.getElementById('summaryBox');
summaryBox.innerHTML =
'<div class="summary-card"><div class="label">上一期特别号</div><div class="value">' + sp + ' (Z' + zone + ')</div></div>' +
'<div class="summary-card"><div class="label">方向预测</div><div class="value orange">' + dirLabel + '</div></div>' +
'<div class="summary-card"><div class="label">候选池</div><div class="value">' + candCount + '个号</div></div>' +
'<div class="summary-card"><div class="label">Top 1</div><div class="value green">#' + top1.number + ' (' + top1.score + '分)</div></div>' +
'<div class="summary-card"><div class="label">Top 3</div><div class="value">' + top3.map(n => '#' + n).join(' ') + '</div></div>' +
'<div class="summary-card"><div class="label">Top 5</div><div class="value">' + top5.map(n => '#' + n).join(' ') + '</div></div>' +
'<div class="summary-card"><div class="label">Top 10</div><div class="value">' + top10.map(n => '#' + n).join(' ') + '</div></div>';
const tbody = document.getElementById('resultBody');
tbody.innerHTML = '';
let renderedCount = 0;
results.forEach((r, idx) => {
const rank = idx + 1;
// 在候选池和排除池之间加分隔线
if (idx === candCount && idx > 0) {
const divTr = document.createElement('tr');
divTr.className = 'section-divider';
divTr.innerHTML = '<td colspan="7" class="divider-label">--- 以下' + (results.length - candCount) + '个号码被排除(方向不符或不在大间隙内) ---</td>';
tbody.appendChild(divTr);
}
let ballClass = 'normal';
if (r.status === 'candidate') {
if (rank === 1) ballClass = 'top1';
else if (rank <= 3) ballClass = 'top3';
else if (rank <= 5) ballClass = 'top5';
else if (rank <= 10) ballClass = 'top10';
} else {
ballClass = 'excluded';
}
let scoreClass = 'low';
if (r.status === 'candidate') {
if (r.score >= 4) scoreClass = 'high';
else if (r.score >= 2) scoreClass = 'mid';
else scoreClass = 'low';
} else {
scoreClass = r.score < 0 ? 'negative' : 'excluded';
}
const dirHtml = r.inDir ? '<span class="dir-yes">✓</span>' : '<span class="dir-no">✗</span>';
const zoneClass = 'zone-' + r.zone;
const rankDisplay = r.status === 'candidate' ? rank : '-';
const tr = document.createElement('tr');
tr.innerHTML =
'<td class="rank">' + rankDisplay + '</td>' +
'<td><span class="num-ball ' + ballClass + '">' + r.number + '</span></td>' +
'<td><span class="zone-badge ' + zoneClass + '">Z' + r.zone + '</span></td>' +
'<td>' + dirHtml + '</td>' +
'<td class="reasons">' + r.gapInfo + '</td>' +
'<td class="score ' + scoreClass + '">' + r.score + '</td>' +
'<td class="reasons">' + r.reasons + '</td>';
tbody.appendChild(tr);
renderedCount++;
});
}
</script>
</body>
</html>