Алгоритмы ранжирования в W3A: Как работают умные ленты новостей и комментариев
Погружение в математические основы сортировки контента на примере формул Вильсона и Reddit-style hotness
В современных социальных новостных агрегаторах и платформах пользовательского контента (UGC) критически важно правильно сортировать материалы. Простая сортировка по дате или количеству голосов не работает: новые материалы с высоким качеством тонут в потоке, а старые спорные публикации продолжают висеть в топе.
В проекте W3A используются два продвинутых алгоритма ранжирования:
- Интервал Вильсона (Wilson Score Interval) — для умного ранжирования комментариев
- Алгоритм «горячести» (Hotness) — для динамической сортировки историй
Разберем каждый из них подробно.
Часть 1: Интервал Вильсона для комментариев
p + z²/(2n) - z√[(p(1-p)/n + z²/(4n²)]
confidence = ─────────────────────────────────────
1 + z²/n
Проблема среднего арифметического
Представьте два комментария:
- Комментарий A: 10 голосов «за», 0 «против» (100% положительных)
- Комментарий B: 950 голосов «за», 50 «против» (95% положительных)
Если сортировать по среднему рейтингу, Комментарий A окажется выше, хотя у Комментария B в 95 раз больше голосов и статистически более надежный рейтинг.
Решение — интервал Вильсона.
Как это работает
Формула Вильсона вычисляет нижнюю границу доверительного интервала для биномиального распределения. Проще говоря: она отвечает на вопрос «какой минимальный рейтинг мы можем гарантировать с определенной уверенностью?»
function wilson_score(int $score, int $flags): float
{
$ups = $score + $flags;
$downs = $flags;
$n = $ups + $downs;
if ($n === 0) {
return 0.0;
}
// Z-оценка для 80% доверительного интервала
$z = 1.281551565545;
$p = $ups / $n; // Доля положительных голосов
// Формула Вильсона
$zSquared = $z * $z;
$left = $p + (1 / (2 * $n) * $zSquared);
$right = $z * sqrt(($p * ((1 - $p) / $n)) + ($zSquared / (4 * $n * $n)));
$under = 1.0 + ((1.0 / $n) * $zSquared);
$confidence = ($left - $right) / $under;
return max(0.0, min(1.0, $confidence));
}
Ключевые параметры
- $z = 1.281551565545 — Z-оценка для 80% доверительного интервала (в Reddit используют 1.28)
- $p — доля положительных голосов (upvotes / total)
- $n — общее количество голосов
Почему это лучше?
Пример расчета:
| Комментарий | Upvotes | Flags | % Positive | Wilson Score |
|---|---|---|---|---|
| A | 10 | 0 | 100% | 0.722 |
| B | 950 | 50 | 95% | 0.938 |
| C | 3 | 0 | 100% | 0.463 |
Несмотря на 100% рейтинг, Комментарии A и C получают более низкий Wilson Score из-за малого количества голосов. Комментарий B с 95% и большим числом голосов оказывается выше — это справедливо!
Преимущества подхода
✅ Защита от накрутки: Новые комментарии с 1-2 голосами не попадают в топ
✅ Статистическая надежность: Учитывается размер выборки
✅ Автоматическая фильтрация спама: Флаги сильно снижают рейтинг
✅ Баланс нового и качественного: Хорошие комментарии поднимаются по мере получения голосов
Часть 2: Алгоритм Hotness для историй
hotness = -((sign × log₁₀(max(|score|, 1)) + seconds / 45000) + Σ hotness_mod_tags)
Проблема хронологической сортировки
Простая сортировка по дате (новые сверху) игнорирует качество контента. Сортировка по общему рейтингу favoreт старые публикации, которые просто успели набрать больше голосов.
Решение — комбинированная метрика «горячести».
Как это работает
Алгоритм объединяет логарифм рейтинга с временным затуханием:
function calculate_hotness(int $score, string $createdAt): float
{
// Логарифмическая шкала рейтинга
$order = log10(max(abs($score), 1));
// Знак (положительный/отрицательный рейтинг)
$sign = $score > 0 ? 1 : ($score < 0 ? -1 : 0);
// Epoch для расчета времени (база: 8 декабря 2005 — запуск Reddit)
$epoch = strtotime('2005-12-08');
$seconds = strtotime($createdAt) - $epoch;
// Финальная формула: (логарифм рейтинга) + (время / константа затухания)
return round(($sign * $order) + ($seconds / 45000), 7);
}
Разбор формулы
1. Логарифмическая шкала (log10)
score = 10 → order = 1
score = 100 → order = 2
score = 1000 → order = 3
score = 10000 → order = 4
Логарифм предотвращает доминирование сверхпопулярных материалов. Разница между 10 и 100 голосами важнее, чем между 1000 и 1010.
2. Временной компонент (seconds / 45000)
- 45000 секунд ≈ 12.5 часов — период полураспада «горячести»
- Каждая публикация получает временной бонус, который уменьшается со временем
- Новые материалы автоматически получают преимущество
3. Константа эпохи (8 декабря 2005)
Это дата запуска Reddit. Используется как базовая точка отсчета для совместимости с оригинальной формулой Reddit.
Пример работы
Допустим, сейчас 22 июня 2026 года:
| История | Score | Дата публикации | Hotness Score |
|---|---|---|---|
| A | 100 | 2 часа назад | 4.8921234 |
| B | 500 | 1 день назад | 4.6987654 |
| C | 1000 | 3 дня назад | 4.3010299 |
Несмотря на то, что История C имеет в 10 раз больше голосов чем A, она оказывается ниже из-за возраста. История A «горячее» — она новая и уже набрала хороший рейтинг.
Почему 45000 секунд?
Это эмпирическая константа, подобранная Reddit. Она означает:
- Через 12.5 часов история теряет примерно 1 порядок логарифмической шкалы
- Через 24 часа даже очень популярная история (score=1000) уступит новой с score=10
- Это создает быстро обновляемую ленту, где всегда есть место новому контенту
Преимущества подхода
✅ Баланс старого и нового: Популярные материалы не доминируют вечно
✅ Защита от «вечного топа»: Старые истории плавно опускаются
✅ Стимулирование активности: Новые публикации получают шанс
✅ Логарифмическая справедливость: 10→100 важнее чем 1000→1010
Часть 3: Сравнение с другими платформами
- Hotness: Использует аналогичную формулу с константой 45000
- Comments: Wilson Score с Z=1.28 (80% доверие)
- W3A: Полная совместимость с подходом Reddit
Hacker News
-
Использует формулу:
(p - 1) / (t + 2)^gravity -
Где
p— очки,t— время в часах,gravity≈ 1.8 - Более агрессивное временное затухание
Lobsters
- Комбинация votes, comments и времени
-
Формула:
votes + comments * 0.5с временным множителем - Более мягкое ранжирование
Часть 4: Практические рекомендации
Когда использовать Wilson Score?
✅ Рейтинги с голосами «за/против»
✅ Комментарии и отзывы
✅ Пользовательский контент с модерацией
✅ Любые ситуации с малым количеством голосов
Когда использовать Hotness?
✅ Ленты новостей
✅ Социальные агрегаторы
✅ Платформы с частыми публикациями
✅ Когда важно показывать свежий качественный контент
Настройка под свой проект
Для Wilson Score:
-
Измените
$zдля другого доверительного интервала:-
1.28— 80% (Reddit, W3A) -
1.64— 90% -
1.96— 95% (более консервативно)
-
Для Hotness:
-
Измените делитель
45000:-
25000— быстрее обновление (6 часов полураспада) -
75000— медленнее (24 часа полураспада) -
100000— очень медленно (36 часов)
-
Заключение
Оба алгоритма решают фундаментальную проблему платформ пользовательского контента: как справедливо ранжировать материалы с разным количеством голосов и возрастом.
Интервал Вильсона защищает от статистического шума и накруток, давая преимущество материалам с проверенным качеством.
Алгоритм Hotness создает динамичную ленту, где всегда есть место новому контенту, но качественные материалы получают шанс набрать аудиторию.
Вместе они создают сбалансированную систему ранжирования, которая:
- Показывает релевантный контент
- Борется со спамом и накрутками
- Стимулирует постоянную активность
- Справедлива к новым пользователям
Эти формулы проверены годами работы Reddit, Hacker News и других крупных платформ. Их реализация в W3A — пример использования математически обоснованных подходов вместо интуитивных решений.
Дополнительные ресурсы
- How Not To Sort By Average Rating (Evan Miller)
- Reddit Sorting Algorithms (GitHub)
- Hacker News Ranking Algorithm
- Wilson Score Interval (Wikipedia)
Автор: Евгений К
Дата: 23 июня 2026
Сайт: https://w3a.ru/
В общем, протестирую решение на w3a, после чего его можно будет перенести на LibArea. Там я использую довольно интересные подходы — возможно, на первый взгляд они покажутся сложными, но нужно оценить их эффективность. На Reddit эти методы уже проверены временем, а здесь их ещё предстоит протестировать.
Если внедрять их в libarea, то запросы для центральной страницы придётся полностью переписать. Это большой объём работы. Возможно, стоит привлечь ИИ для оптимизации кода и оценки потенциального выигрыша в производительности. Буду изучать этот вопрос.