Обновить

Комментарии 27

очень круто! поставил бы плюс если б мог.

такой вопрос - а этот принцип используется в криптографии для быстрого поиска простых чисел или там есть еше более быстрые, но не очевидные лайфхаки?

делать поиск чисел только по осям возможных спиралей это в разы оптимальнее чем перебирать все числа я считаю. причем формула расчета спиралей позволяет быстро локализовать квадрат поиска если нам нужно например тридцатизначное простое число

как минимум для микроконтроллеров я вижу возможность для быстрой генерации безопасных одноразовых токенов, без оверхеда по расчетам и специальных крипточипров (простая алгебра и геометрия сейчас зачастую вшита уже в камни для оптимизации операций)

поиск чисел только по осям возможных спиралей

Это просто решето Эратосфена, в котором мы использовали только 2 и 11, или 2, 5 и 71. Ничего оптимального в этом нет. Если уж вы зачем-то хотите ограничиться двумя простыми числами, то оптимальнее всего использовать классы вычетов по 6, а не по 44 (так вы оставите для поиска 1/3 часть чисел, а не 5/11). А если тремя, то классы вычетов по 30, а не по 710 (так вы оставите 4/15 чисел, а не 28/71). Но гораздо проще не мучиться и использовать полноценное решето Эратосфена.

Поиск по осям это просто хардкод решета Эратосфена с заранее выкинутыми делителями. В реальной криптографии нужны числа в сотни бит, там этот геометрический подход сожрет всю память на первых же итерациях

НЛО прилетело и опубликовало эту надпись здесь

Похоже именно она.

НЛО прилетело и опубликовало эту надпись здесь

Если числа располагать в виде спирали, то почему удивляться что они расположены по спирали?

Любая достаточно развитая технология неотличима от магии. Для кого-то даже школьная математика уже является достаточно развитой для этого :)

Я тоже так сначала подумал, но интересность в том, что числа располагаются на одной спирали, а визуально выстраиваются в другие. Сначала потому, что 6 радиан примерно равны одному полному обороту, а потом потому что 44 радиана примерно равны 7 полным оборотам.

Интересно, что же будет, если вместо (p, p) взять (p, πp)…

На правой (положительной) полуоси Ох будут чётные числа. На левой (отрицательной) — нечётные. Если оставить только простые числа, то справа будет одинокая двойка, а слева все остальные простые числа.

Всё образует спираль, главное правильно нарисовать...

Магма течёт в наших венах, раскаляя сердца (с)

<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Universe Rebirth — чёрная дыра и восстановление Вселенной</title>
  <style>
    :root {
      --bg: #030712;
      --panel: rgba(15, 23, 42, .78);
      --text: #eaf0ff;
      --muted: #9ba8c6;
      --line: rgba(255,255,255,.12);
      --blue: #7aa2ff;
      --cyan: #5eead4;
      --violet: #a78bfa;
      --yellow: #fde68a;
      --red: #fb7185;
    }

    * { box-sizing: border-box; }

    body {
      margin: 0;
      min-height: 100vh;
      overflow: hidden;
      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      color: var(--text);
      background: var(--bg);
    }

    canvas {
      position: fixed;
      inset: 0;
      width: 100vw;
      height: 100vh;
      display: block;
      background: radial-gradient(circle at center, #0b1020 0%, #030712 56%, #01030a 100%);
    }

    .panel {
      position: fixed;
      left: 20px;
      top: 20px;
      width: min(430px, calc(100vw - 40px));
      max-height: calc(100vh - 40px);
      overflow: auto;
      padding: 18px;
      border: 1px solid var(--line);
      border-radius: 24px;
      background: var(--panel);
      backdrop-filter: blur(18px);
      box-shadow: 0 28px 80px rgba(0,0,0,.42);
    }

    h1 {
      margin: 0 0 8px;
      font-size: 28px;
      line-height: 1;
      letter-spacing: -0.055em;
    }

    .lead {
      margin: 0 0 16px;
      color: var(--muted);
      font-size: 13px;
      line-height: 1.5;
    }

    .formula {
      margin: 0 0 16px;
      padding: 12px;
      border: 1px solid rgba(255,255,255,.1);
      border-radius: 16px;
      background: rgba(0,0,0,.24);
      color: #dbeafe;
      font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
      font-size: 12px;
      line-height: 1.55;
      white-space: pre-wrap;
    }

    .grid { display: grid; gap: 12px; }

    .row {
      display: grid;
      grid-template-columns: 1fr auto;
      gap: 12px;
      align-items: center;
    }

    label {
      display: block;
      color: var(--muted);
      font-size: 12px;
      margin-bottom: 6px;
    }

    input[type="range"] {
      width: 100%;
      accent-color: #7aa2ff;
    }

    select, button {
      width: 100%;
      border: 1px solid var(--line);
      border-radius: 14px;
      background: rgba(15,23,42,.92);
      color: var(--text);
      padding: 10px 12px;
      font-size: 14px;
      outline: none;
    }

    .value {
      min-width: 66px;
      text-align: right;
      color: #dbeafe;
      font-size: 12px;
      font-variant-numeric: tabular-nums;
    }

    button {
      border: 0;
      cursor: pointer;
      font-weight: 800;
      background: linear-gradient(135deg, #4f7cff, #8b5cf6 52%, #14b8a6);
      box-shadow: 0 18px 40px rgba(79,124,255,.22);
      transition: transform .15s ease, filter .15s ease;
      margin-top: 2px;
    }

    button:hover { transform: translateY(-1px); filter: brightness(1.08); }

    .two {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
    }

    .stats {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 10px;
      margin-top: 14px;
    }

    .stat {
      padding: 10px;
      border: 1px solid rgba(255,255,255,.09);
      border-radius: 16px;
      background: rgba(255,255,255,.045);
    }

    .stat .k {
      color: var(--muted);
      font-size: 11px;
      margin-bottom: 2px;
    }

    .stat .v {
      font-size: 17px;
      font-weight: 800;
      letter-spacing: -0.03em;
    }

    .hint {
      margin-top: 14px;
      color: var(--muted);
      font-size: 12px;
      line-height: 1.45;
    }

    .pill {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      border: 1px solid rgba(255,255,255,.1);
      border-radius: 999px;
      padding: 4px 8px;
      background: rgba(255,255,255,.05);
      color: #dbeafe;
      font-size: 11px;
      margin: 0 4px 4px 0;
    }

    .rightPanel {
      position: fixed;
      right: 20px;
      bottom: 20px;
      width: min(390px, calc(100vw - 40px));
      padding: 14px;
      border: 1px solid var(--line);
      border-radius: 22px;
      background: var(--panel);
      backdrop-filter: blur(18px);
      box-shadow: 0 28px 80px rgba(0,0,0,.42);
    }

    .rightPanel h2 {
      margin: 0 0 8px;
      font-size: 16px;
      letter-spacing: -0.03em;
    }

    .rightPanel p {
      margin: 0;
      color: var(--muted);
      font-size: 12px;
      line-height: 1.45;
    }

    .phaseName {
      color: #fde68a;
      font-weight: 900;
    }

    @media (max-width: 860px) {
      body { overflow: auto; }
      canvas { position: fixed; }
      .panel, .rightPanel {
        position: relative;
        left: auto;
        right: auto;
        top: auto;
        bottom: auto;
        width: calc(100vw - 24px);
        margin: 12px;
      }
      .rightPanel { margin-top: 0; }
    }
  </style>
</head>
<body>
  <canvas id="universe"></canvas>

  <section class="panel">
    <h1>Universe Rebirth</h1>
    <p class="lead">
      Художественная математическая модель: Вселенная расширяется, схлопывается в чёрную дыру,
      почти исчезает за горизонтом событий, а затем в последний момент восстанавливается.
    </p>

    <div class="formula">base:     r = expansion · sqrt(i) + wave · sin(i·k + t)
collapse: r = mix(baseR, blackHoleR, collapseFactor)
black:    θ = θ + vortex · collapseFactor²
rebirth:  r = baseR · rebirthPulse + shockWave</div>

    <div class="grid">
      <div>
        <label for="preset">Сценарий</label>
        <select id="preset">
          <option value="rebirth">Чёрная дыра → восстановление</option>
          <option value="slow">Медленное схлопывание</option>
          <option value="violent">Жёсткий гравитационный шторм</option>
          <option value="calm">Мягкая космическая пульсация</option>
        </select>
      </div>

      <div>
        <label for="particles">Частицы</label>
        <div class="row">
          <input id="particles" type="range" min="2000" max="70000" step="1000" value="26000">
          <div id="particlesVal" class="value">26000</div>
        </div>
      </div>

      <div>
        <label for="cycle">Длительность цикла, секунд</label>
        <div class="row">
          <input id="cycle" type="range" min="8" max="60" step="1" value="24">
          <div id="cycleVal" class="value">24</div>
        </div>
      </div>

      <div>
        <label for="expansion">Базовое расширение</label>
        <div class="row">
          <input id="expansion" type="range" min="0.8" max="8" step="0.1" value="3.4">
          <div id="expansionVal" class="value">3.4</div>
        </div>
      </div>

      <div>
        <label for="blackHole">Сила чёрной дыры</label>
        <div class="row">
          <input id="blackHole" type="range" min="0" max="10" step="0.1" value="7.4">
          <div id="blackHoleVal" class="value">7.4</div>
        </div>
      </div>

      <div>
        <label for="vortex">Закручивание</label>
        <div class="row">
          <input id="vortex" type="range" min="0" max="18" step="0.1" value="9.8">
          <div id="vortexVal" class="value">9.8</div>
        </div>
      </div>

      <div>
        <label for="wave">Гравитационные волны</label>
        <div class="row">
          <input id="wave" type="range" min="0" max="100" step="1" value="24">
          <div id="waveVal" class="value">24</div>
        </div>
      </div>

      <div>
        <label for="rebirthPower">Сила восстановления</label>
        <div class="row">
          <input id="rebirthPower" type="range" min="0" max="10" step="0.1" value="6.5">
          <div id="rebirthPowerVal" class="value">6.5</div>
        </div>
      </div>

      <div class="two">
        <button id="randomBtn">Случайная Вселенная</button>
        <button id="pauseBtn">Пауза</button>
      </div>
    </div>

    <div class="stats">
      <div class="stat"><div class="k">Фаза</div><div id="phase" class="v phaseName">—</div></div>
      <div class="stat"><div class="k">Схлопывание</div><div id="collapseStat" class="v">0%</div></div>
      <div class="stat"><div class="k">Горизонт</div><div id="horizonStat" class="v">—</div></div>
      <div class="stat"><div class="k">FPS</div><div id="fps" class="v">—</div></div>
    </div>

    <p class="hint">
      <span class="pill">Колесо мыши — масштаб</span>
      <span class="pill">Движение мыши — параллакс</span>
      <span class="pill">Цикл сам повторяется</span>
    </p>
  </section>

  <aside class="rightPanel">
    <h2>Как работает эффект</h2>
    <p>
      На фазе схлопывания координаты частиц смешиваются с координатами около центра. Чем ближе конец цикла,
      тем сильнее закручивание и темнее ядро. В момент почти полного поглощения включается «отскок»:
      радиус резко восстанавливается, появляется ударная волна, и структура Вселенной собирается обратно.
    </p>
  </aside>

  <script>
    const canvas = document.getElementById('universe');
    const ctx = canvas.getContext('2d', { alpha: false });

    const els = {
      preset: document.getElementById('preset'),
      particles: document.getElementById('particles'),
      cycle: document.getElementById('cycle'),
      expansion: document.getElementById('expansion'),
      blackHole: document.getElementById('blackHole'),
      vortex: document.getElementById('vortex'),
      wave: document.getElementById('wave'),
      rebirthPower: document.getElementById('rebirthPower'),
      randomBtn: document.getElementById('randomBtn'),
      pauseBtn: document.getElementById('pauseBtn'),
      phase: document.getElementById('phase'),
      collapseStat: document.getElementById('collapseStat'),
      horizonStat: document.getElementById('horizonStat'),
      fps: document.getElementById('fps')
    };

    const valueEls = {
      particles: document.getElementById('particlesVal'),
      cycle: document.getElementById('cycleVal'),
      expansion: document.getElementById('expansionVal'),
      blackHole: document.getElementById('blackHoleVal'),
      vortex: document.getElementById('vortexVal'),
      wave: document.getElementById('waveVal'),
      rebirthPower: document.getElementById('rebirthPowerVal')
    };

    const GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));

    let W = 0;
    let H = 0;
    let DPR = 1;
    let time = 0;
    let paused = false;
    let zoom = 1;
    let mouseX = 0;
    let mouseY = 0;
    let lastTime = performance.now();
    let fpsSmooth = 60;

    const presets = {
      rebirth: {
        particles: 26000,
        cycle: 24,
        expansion: 3.4,
        blackHole: 7.4,
        vortex: 9.8,
        wave: 24,
        rebirthPower: 6.5
      },
      slow: {
        particles: 30000,
        cycle: 42,
        expansion: 3.0,
        blackHole: 5.8,
        vortex: 6.2,
        wave: 18,
        rebirthPower: 4.5
      },
      violent: {
        particles: 46000,
        cycle: 18,
        expansion: 4.9,
        blackHole: 9.6,
        vortex: 16.5,
        wave: 82,
        rebirthPower: 9.2
      },
      calm: {
        particles: 18000,
        cycle: 30,
        expansion: 2.6,
        blackHole: 4.8,
        vortex: 4.5,
        wave: 14,
        rebirthPower: 3.8
      }
    };

    function resize() {
      DPR = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));
      W = window.innerWidth;
      H = window.innerHeight;
      canvas.width = Math.floor(W * DPR);
      canvas.height = Math.floor(H * DPR);
      canvas.style.width = W + 'px';
      canvas.style.height = H + 'px';
      ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
    }

    function num(id) {
      return Number(els[id].value);
    }

    function clamp01(x) {
      return Math.min(1, Math.max(0, x));
    }

    function smoothstep(edge0, edge1, x) {
      const v = clamp01((x - edge0) / Math.max(1e-9, edge1 - edge0));
      return v * v * (3 - 2 * v);
    }

    function mix(a, b, t) {
      return a + (b - a) * t;
    }

    function syncLabels() {
      for (const key of Object.keys(valueEls)) {
        valueEls[key].textContent = els[key].value;
      }
    }

    function applyPreset(name) {
      const p = presets[name];
      for (const key of Object.keys(p)) els[key].value = p[key];
      syncLabels();
    }

    function randomUniverse() {
      els.particles.value = Math.round((8000 + Math.random() * 60000) / 1000) * 1000;
      els.cycle.value = Math.round(10 + Math.random() * 42);
      els.expansion.value = (1.2 + Math.random() * 6.5).toFixed(1);
      els.blackHole.value = (3 + Math.random() * 7).toFixed(1);
      els.vortex.value = (2 + Math.random() * 16).toFixed(1);
      els.wave.value = Math.round(Math.random() * 100);
      els.rebirthPower.value = (2 + Math.random() * 8).toFixed(1);
      syncLabels();
    }

    function getPhase(p) {
      // p — прогресс внутри цикла 0..1
      const expansionPhase = 1 - smoothstep(0.10, 0.38, p);
      const collapsePhase = smoothstep(0.28, 0.72, p) * (1 - smoothstep(0.82, 0.96, p));
      const nearSingularity = smoothstep(0.66, 0.82, p) * (1 - smoothstep(0.84, 0.91, p));
      const rebirth = smoothstep(0.78, 0.91, p) * (1 - smoothstep(0.91, 1.0, p));
      const restoration = smoothstep(0.86, 1.0, p);

      let name = 'расширение';
      if (p > 0.28 && p <= 0.66) name = 'схлопывание';
      if (p > 0.66 && p <= 0.82) name = 'горизонт';
      if (p > 0.82 && p <= 0.91) name = 'отскок';
      if (p > 0.91) name = 'восстановление';

      return { expansionPhase, collapsePhase, nearSingularity, rebirth, restoration, name };
    }

    function colorFor(index, zNorm, radiusNorm, phase, p) {
      let hue = (210 + 90 * Math.sin(index * 0.0008 + time * 0.5) + 80 * radiusNorm) % 360;

      if (phase.nearSingularity > 0.1) {
        hue = mix(hue, 18, phase.nearSingularity);
      }

      if (phase.rebirth > 0.1) {
        hue = mix(hue, 190 + 80 * Math.sin(index), phase.rebirth);
      }

      const sat = 70 + 25 * zNorm;
      const light = 48 + 30 * zNorm + phase.rebirth * 18 - phase.collapsePhase * 10;
      return `hsl(${hue} ${sat}% ${light}%)`;
    }

    function drawBackground(phase) {
      const cx = W / 2;
      const cy = H / 2;
      const danger = phase.nearSingularity;
      const rebirth = phase.rebirth;

      const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(W, H) * 0.76);
      grad.addColorStop(0, `rgba(${Math.round(mix(11, 255, rebirth))}, ${Math.round(mix(16, 255, rebirth))}, ${Math.round(mix(32, 255, rebirth))}, 1)`);
      grad.addColorStop(0.08, danger > 0.2 ? '#050008' : '#0b1020');
      grad.addColorStop(0.48, danger > 0.2 ? '#09020a' : '#030712');
      grad.addColorStop(1, '#01030a');
      ctx.fillStyle = grad;
      ctx.fillRect(0, 0, W, H);

      // Сетка пространства-времени, которая искажается к центру.
      ctx.save();
      ctx.globalAlpha = 0.10 + rebirth * 0.28;
      ctx.strokeStyle = rebirth > 0.2 ? 'rgba(94,234,212,.38)' : 'rgba(122,162,255,.16)';
      ctx.lineWidth = 1;
      const maxR = Math.min(W, H) * 0.50 * zoom;
      const wobble = Math.sin(time * 4) * 8 * phase.collapsePhase;
      for (let i = 1; i <= 9; i++) {
        ctx.beginPath();
        ctx.arc(cx + mouseX * wobble, cy + mouseY * wobble, maxR * i / 9, 0, Math.PI * 2);
        ctx.stroke();
      }
      ctx.restore();
    }

    function drawBlackHole(cx, cy, phase, baseScale) {
      const collapse = phase.collapsePhase;
      const danger = phase.nearSingularity;
      const rebirth = phase.rebirth;
      const blackHole = num('blackHole');

      const horizon = (38 + blackHole * 9 + danger * 80) * baseScale;
      const diskR = horizon * (2.5 + collapse * 1.8);
      const angle = time * (1.5 + num('vortex') * 0.08);

      els.horizonStat.textContent = Math.round(horizon) + ' px';

      ctx.save();
      ctx.translate(cx, cy);
      ctx.rotate(angle);
      ctx.globalCompositeOperation = 'lighter';

      // Аккреционный диск.
      for (let i = 0; i < 7; i++) {
        ctx.globalAlpha = (0.05 + collapse * 0.09 + danger * 0.06) * (1 - i * 0.08);
        ctx.strokeStyle = i % 2 ? 'rgba(251,113,133,.75)' : 'rgba(250,204,21,.72)';
        ctx.lineWidth = Math.max(1, 2.5 * baseScale);
        ctx.beginPath();
        ctx.ellipse(0, 0, diskR * (1 + i * 0.13), diskR * (0.23 + i * 0.018), 0, 0, Math.PI * 2);
        ctx.stroke();
      }

      ctx.restore();

      // Горизонт событий — абсолютная чёрная область.
      ctx.save();
      ctx.globalCompositeOperation = 'source-over';
      const shadow = ctx.createRadialGradient(cx, cy, 0, cx, cy, horizon * 2.4);
      shadow.addColorStop(0, 'rgba(0,0,0,1)');
      shadow.addColorStop(0.55, 'rgba(0,0,0,.98)');
      shadow.addColorStop(0.72, 'rgba(15,3,18,.8)');
      shadow.addColorStop(1, 'rgba(0,0,0,0)');
      ctx.fillStyle = shadow;
      ctx.beginPath();
      ctx.arc(cx, cy, horizon * 2.4, 0, Math.PI * 2);
      ctx.fill();

      // Светящийся край горизонта.
      ctx.globalCompositeOperation = 'lighter';
      ctx.globalAlpha = 0.35 + danger * 0.45 - rebirth * 0.2;
      ctx.strokeStyle = 'rgba(255,220,160,.8)';
      ctx.lineWidth = 2.2 * baseScale;
      ctx.beginPath();
      ctx.arc(cx, cy, horizon * 1.02, 0, Math.PI * 2);
      ctx.stroke();
      ctx.restore();
    }

    function drawRebirthShock(cx, cy, phase, baseScale) {
      if (phase.rebirth <= 0.01 && phase.restoration <= 0.01) return;

      const power = num('rebirthPower');
      const pulse = phase.rebirth;
      const restore = phase.restoration;
      const shockR = (80 + pulse * Math.min(W, H) * 0.62 + restore * Math.min(W, H) * 0.18) * baseScale;

      ctx.save();
      ctx.globalCompositeOperation = 'lighter';

      const grad = ctx.createRadialGradient(cx, cy, Math.max(1, shockR * 0.72), cx, cy, shockR * 1.08);
      grad.addColorStop(0, 'rgba(94,234,212,0)');
      grad.addColorStop(0.5, `rgba(125,211,252,${0.12 + pulse * 0.34})`);
      grad.addColorStop(0.72, `rgba(255,255,255,${0.10 + pulse * 0.55})`);
      grad.addColorStop(1, 'rgba(167,139,250,0)');
      ctx.fillStyle = grad;
      ctx.beginPath();
      ctx.arc(cx, cy, shockR * (1 + power * 0.015), 0, Math.PI * 2);
      ctx.fill();

      ctx.globalAlpha = 0.35 * pulse;
      ctx.strokeStyle = 'rgba(255,255,255,.9)';
      ctx.lineWidth = 2.5 * baseScale;
      ctx.beginPath();
      ctx.arc(cx, cy, shockR, 0, Math.PI * 2);
      ctx.stroke();

      ctx.restore();
    }

    function drawUniverse(dt) {
      const particles = num('particles');
      const cycle = num('cycle');
      const expansion = num('expansion');
      const blackHole = num('blackHole');
      const vortex = num('vortex');
      const wave = num('wave');
      const rebirthPower = num('rebirthPower');

      if (!paused) time += dt * 0.001;

      const p = (time % cycle) / cycle;
      const phase = getPhase(p);

      els.phase.textContent = phase.name;
      els.collapseStat.textContent = Math.round(phase.collapsePhase * 100) + '%';

      drawBackground(phase);

      const cx = W / 2 + mouseX * 26;
      const cy = H / 2 + mouseY * 26;
      const baseScale = Math.min(W, H) / 920 * zoom;
      const k = 0.014 + blackHole * 0.0018;
      const q = 0.009 + wave * 0.00012;

      const collapse = phase.collapsePhase;
      const danger = phase.nearSingularity;
      const rebirth = phase.rebirth;
      const restore = phase.restoration;

      ctx.save();
      ctx.globalCompositeOperation = 'lighter';

      for (let i = 1; i <= particles; i++) {
        const sqrtI = Math.sqrt(i);
        const baseR = (expansion * sqrtI + wave * Math.sin(i * k + time * 1.6)) * baseScale;
        const baseTheta = i * GOLDEN_ANGLE + 0.42 * Math.sin(time + i * q);

        // Во время схлопывания частица стремится к чёрной дыре, но не строго по прямой — она закручивается.
        const sinkR = (10 + 42 * Math.sin(i * 0.019 + time * 3) ** 2) * baseScale;
        const collapseR = mix(baseR, sinkR, collapse);
        const twistedTheta = baseTheta + vortex * collapse * collapse * (1.5 + i / particles * 5.5) + time * collapse * 2.4;

        // В момент восстановления радиус возвращается к базовой структуре через ударную волну.
        const waveFront = smoothstep(0.0, 1.0, rebirth);
        const particleDelay = i / particles;
        const rebirthGate = smoothstep(particleDelay * 0.55, particleDelay * 0.55 + 0.22, waveFront);
        const shock = rebirthPower * 30 * Math.sin((particleDelay - waveFront) * 18) * Math.exp(-Math.abs(particleDelay - waveFront) * 6) * baseScale;

        let r = mix(collapseR, baseR + shock, Math.max(rebirthGate, restore));
        r *= 1 + 0.08 * Math.sin(time * 3 + i * 0.005) * (rebirth + restore);

        let theta = mix(twistedTheta, baseTheta + 0.35 * Math.sin(time * 2 + i * q), Math.max(rebirthGate, restore));

        const depth = 120 * Math.sin(i * q + time * 1.4);
        const zNorm = (Math.sin(i * q + time * 1.4) + 1) / 2;

        const x = cx + r * Math.cos(theta) + mouseX * depth * 0.13;
        const y = cy + r * Math.sin(theta) + mouseY * depth * 0.13;

        const radiusNorm = Math.min(1, r / (Math.min(W, H) * 0.58));
        const swallowed = collapse > 0.7 && r < (54 + blackHole * 9) * baseScale;
        const swallowFade = swallowed ? 0.25 : 1;

        const size = Math.max(0.38, 0.52 + zNorm * 1.55 - radiusNorm * 0.18 + rebirth * 0.9);
        const alpha = Math.max(0.02, (0.13 + zNorm * 0.62 - radiusNorm * 0.15 + rebirth * 0.4 - danger * 0.08) * swallowFade);

        ctx.globalAlpha = alpha;
        ctx.fillStyle = colorFor(i, zNorm, radiusNorm, phase, p);
        ctx.beginPath();
        ctx.arc(x, y, size, 0, Math.PI * 2);
        ctx.fill();

        // Следы падения в чёрную дыру.
        if (collapse > 0.28 && i % 17 === 0) {
          const tail = 1 + collapse * 18;
          const tx = cx + (r + tail) * Math.cos(theta - collapse * 0.18);
          const ty = cy + (r + tail) * Math.sin(theta - collapse * 0.18);
          ctx.globalAlpha = alpha * 0.22 * collapse;
          ctx.strokeStyle = colorFor(i, zNorm, radiusNorm, phase, p);
          ctx.lineWidth = Math.max(0.35, size * 0.45);
          ctx.beginPath();
          ctx.moveTo(x, y);
          ctx.lineTo(tx, ty);
          ctx.stroke();
        }
      }

      ctx.restore();

      drawBlackHole(cx, cy, phase, baseScale);
      drawRebirthShock(cx, cy, phase, baseScale);

      // Финальная вспышка в момент отскока.
      if (rebirth > 0.05) {
        ctx.save();
        ctx.globalCompositeOperation = 'lighter';
        ctx.globalAlpha = 0.12 + rebirth * 0.28;
        ctx.fillStyle = 'rgba(255,255,255,.65)';
        ctx.fillRect(0, 0, W, H);
        ctx.restore();
      }
    }

    function frame(now) {
      const dt = now - lastTime;
      lastTime = now;
      fpsSmooth = fpsSmooth * 0.92 + (1000 / Math.max(1, dt)) * 0.08;
      els.fps.textContent = Math.round(fpsSmooth);
      drawUniverse(dt);
      requestAnimationFrame(frame);
    }

    for (const key of ['particles', 'cycle', 'expansion', 'blackHole', 'vortex', 'wave', 'rebirthPower']) {
      els[key].addEventListener('input', syncLabels);
    }

    els.preset.addEventListener('change', () => applyPreset(els.preset.value));
    els.randomBtn.addEventListener('click', randomUniverse);
    els.pauseBtn.addEventListener('click', () => {
      paused = !paused;
      els.pauseBtn.textContent = paused ? 'Продолжить' : 'Пауза';
    });

    window.addEventListener('resize', resize);
    window.addEventListener('mousemove', (e) => {
      mouseX = (e.clientX / Math.max(1, W) - 0.5) * 2;
      mouseY = (e.clientY / Math.max(1, H) - 0.5) * 2;
    });

    window.addEventListener('wheel', (e) => {
      e.preventDefault();
      const factor = e.deltaY < 0 ? 1.08 : 0.92;
      zoom = Math.min(5, Math.max(0.35, zoom * factor));
    }, { passive: false });

    resize();
    applyPreset('rebirth');
    syncLabels();
    requestAnimationFrame(frame);
  </script>
</body>
</html>

Пэйстнул не в тот чат?)

Не тактично вводить математику такие "перлы". Существуют ссылки. Оформляйте ими и возьмите в дальнейшем это за правило. Просто необходимо уважать любое сообщество, тем более вы у автора в гостях, поэтому нельзя выглядеть в такой ситуации "аля" теоретиком, философом, а оказываться просто - невеждой. Или создайте на основе своих изложений, свой личный, привлекающий внимание, пост. Желаю успехов.

НЛО прилетело и опубликовало эту надпись здесь

Геометрический факт («6 радиан близко к полному обороту») скрещивается с алгебраическим фактом («простые числа не могут принадлежать классам вычетов с общими делителями»). Результат скрещивания — нетривиальное объяснение паттерна.
Трёхмерное обобщение («паттерн знака бесконечности») осталось необъяснённым.

Насколько же глубоко можно зарыться в обычный числовой ряд, если есть уйма свободного времени.. Жаль только что трехмерную бесконечность так и оставили без объяснения)

простые числа определяются целостностью всех чисел всех их отношений между собой , то есть из недостижимого рациональным умом конца бесконечности

Осталось отобразить распределение простых чисел в 3D, 5D, 7D, 11D, ... проекциях.
Картины будут ощеломляюще прекрасными рапределениями.

Держи!
Держи!
НЛО прилетело и опубликовало эту надпись здесь
Держи!
Держи!
НЛО прилетело и опубликовало эту надпись здесь

Спасибо за статью! Очень интересный подход к визуализации распределения простых чисел.

Я сам раньше много возился с простыми числами и начинал с классического ряда 2k+1 — то есть с нечётных чисел. Довольно быстро понял, что этого недостаточно. Чтобы хоть как-то подобраться к пониманию того, как простые числа распределены на самом деле, пришлось переключиться на числа, делящиеся на 2, потом на 3, потом на 4 и так далее. Постепенно, шаг за шагом.

Ведь простые числа не существуют в вакууме. Они всегда окружены числами, которые делятся на что-то ещё, кроме себя и единицы. И без анализа этой «составной среды» их природу не понять. Ваш подход со спиралями и классами вычетов по модулю 44, 710 — в каком-то смысле про то же самое: простые видны только на фоне того, что ими не является.

Вопрос автору: не пробовали ли вы строить аналогичные спирали не для самих простых чисел, а для их «расстояний до ближайшего составного»? Мне кажется, это могло бы дать дополнительный слой закономерностей.

Расстояние от простого до ближайшего составного всегда 1 (кроме 2, где расстояние 2). Или я что-то неправильно понял?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
ruvds.com
Дата регистрации
Дата основания
Численность
11–30 человек
Местоположение
Россия
Представитель
ruvds