PHP 信息流排序算法

wen PHP项目 4

本文目录导读:

PHP 信息流排序算法

  1. 基础排序算法实现
  2. Reddit热门排序算法
  3. 综合权重排序算法
  4. 基于用户偏好的个性化排序
  5. 分页与性能优化
  6. 缓存策略
  7. 完整示例
  8. 算法选择建议

我来详细介绍PHP信息流排序算法的实现方案:

基础排序算法实现

简单时间排序

<?php
class FeedSorter {
    // 按时间倒序排列
    public function sortByTime(array $feeds): array {
        usort($feeds, function($a, $b) {
            return strtotime($b['created_at']) - strtotime($a['created_at']);
        });
        return $feeds;
    }
}
?>

热度加权排序(Hacker News算法)

<?php
class HackerNewsRanker {
    /**
     * Hacker News 热度算法
     * Score = (P-1) / (T+2)^G
     * P = 点赞数,T = 发布时间(小时),G = 重力因子(通常1.8)
     */
    public function calculateScore(int $points, int $hoursAgo, float $gravity = 1.8): float {
        if ($points <= 0) return 0;
        return ($points - 1) / pow(($hoursAgo + 2), $gravity);
    }
    public function sortByHackerNews(array $feeds): array {
        $now = time();
        foreach ($feeds as &$feed) {
            $hoursAgo = ($now - strtotime($feed['created_at'])) / 3600;
            $feed['score'] = $this->calculateScore(
                $feed['likes'] ?? 0, 
                $hoursAgo
            );
        }
        usort($feeds, function($a, $b) {
            return $b['score'] <=> $a['score'];
        });
        return $feeds;
    }
}
?>

Reddit热门排序算法

<?php
class RedditRanker {
    /**
     * Reddit 热门排序
     * Score = log10(max(ups, 1)) + sign(ups-downs) * seconds/45000
     */
    public function hotScore(int $ups, int $downs, string $createdAt): float {
        $seconds = max(1, strtotime($createdAt) - 1134028003);
        $score = log10(max($ups, 1)) + ($ups - $downs) * $seconds / 45000;
        return $score;
    }
    public function sortByHot(array $feeds): array {
        foreach ($feeds as &$feed) {
            $ups = $feed['likes'] ?? 0;
            $downs = $feed['dislikes'] ?? 0;
            $feed['score'] = $this->hotScore($ups, $downs, $feed['created_at']);
        }
        usort($feeds, function($a, $b) {
            return $b['score'] <=> $a['score'];
        });
        return $feeds;
    }
}
?>

综合权重排序算法

<?php
class ComprehensiveRanker {
    private $weights = [
        'time' => 0.4,
        'likes' => 0.3,
        'comments' => 0.2,
        'shares' => 0.1
    ];
    // 配置权重
    public function setWeights(array $weights): void {
        $this->weights = array_merge($this->weights, $weights);
    }
    /**
     * 计算综合得分
     */
    public function calculateScore(array $feed): float {
        // 时间衰减因子(48小时衰减)
        $timeScore = $this->timeDecay($feed['created_at'], 48);
        // 互动得分归一化
        $likeScore = $this->normalize($feed['likes'] ?? 0);
        $commentScore = $this->normalize($feed['comments'] ?? 0);
        $shareScore = $this->normalize($feed['shares'] ?? 0);
        return (
            $this->weights['time'] * $timeScore +
            $this->weights['likes'] * $likeScore +
            $this->weights['comments'] * $commentScore +
            $this->weights['shares'] * $shareScore
        );
    }
    // 时间衰减函数(指数衰减)
    private function timeDecay(string $timestamp, int $halfLifeHours): float {
        $ageHours = (time() - strtotime($timestamp)) / 3600;
        return pow(0.5, $ageHours / $halfLifeHours);
    }
    // 归一化(对数归一化)
    private function normalize(int $value): float {
        if ($value <= 0) return 0;
        return log10($value + 1) / log10(10001); // 上限10000
    }
    public function sortFeeds(array $feeds): array {
        foreach ($feeds as &$feed) {
            $feed['score'] = $this->calculateScore($feed);
        }
        usort($feeds, function($a, $b) {
            return $b['score'] <=> $a['score'];
        });
        return $feeds;
    }
}
?>

基于用户偏好的个性化排序

<?php
class PersonalizedRanker {
    private $userInterests = [];
    private $userHistory = [];
    public function __construct(array $userInterests, array $userHistory) {
        $this->userInterests = $userInterests;   // 兴趣标签权重
        $this->userHistory = $userHistory;       // 历史互动记录
    }
    /**
     * 个性化推荐排序
     */
    public function personalizedScore(array $feed): float {
        $baseScore = 0;
        $interestScore = 0;
        $historyScore = 0;
        // 1. 内容标签匹配度
        if (isset($feed['tags'])) {
            foreach ($feed['tags'] as $tag) {
                if (isset($this->userInterests[$tag])) {
                    $interestScore += $this->userInterests[$tag];
                }
            }
        }
        // 2. 历史行为偏好
        if (isset($this->userHistory[$feed['author_id']])) {
            $historyScore = $this->userHistory[$feed['author_id']] * 0.1;
        }
        // 3. 内容质量分(结合其他算法)
        $qualityScore = $this->calculateQualityScore($feed);
        // 综合计算
        $baseScore = $interestScore * 0.5 + $historyScore * 0.2 + $qualityScore * 0.3;
        return $baseScore;
    }
    private function calculateQualityScore(array $feed): float {
        // 结合时间衰减和互动量
        $timeFactor = $this->timeDecay($feed['created_at']);
        $interactionFactor = log10(($feed['likes'] ?? 0) + ($feed['comments'] ?? 0) + 1);
        return $timeFactor * $interactionFactor;
    }
    private function timeDecay(string $timestamp): float {
        $hours = (time() - strtotime($timestamp)) / 3600;
        return 1 / (1 + $hours * 0.01);
    }
}
?>

分页与性能优化

<?php
class FeedPaginator {
    /**
     * 游标分页(比传统页码分页更高效)
     */
    public function getFeedsWithCursor(PDO $pdo, ?string $cursor, int $limit = 20): array {
        $sql = "SELECT * FROM feeds ";
        if ($cursor) {
            // 游标包含时间戳和ID
            [$cursorTime, $cursorId] = explode('_', $cursor);
            $sql .= "WHERE (created_at < :time) OR (created_at = :time AND id < :id) ";
        }
        $sql .= "ORDER BY created_at DESC, id DESC LIMIT :limit";
        $stmt = $pdo->prepare($sql);
        if ($cursor) {
            $stmt->bindValue(':time', $cursorTime);
            $stmt->bindValue(':id', $cursorId);
        }
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->execute();
        $feeds = $stmt->fetchAll(PDO::FETCH_ASSOC);
        // 生成下一次的游标
        $nextCursor = null;
        if (count($feeds) == $limit) {
            $last = end($feeds);
            $nextCursor = $last['created_at'] . '_' . $last['id'];
        }
        return [
            'feeds' => $feeds,
            'next_cursor' => $nextCursor
        ];
    }
}
?>

缓存策略

<?php
class FeedCacheManager {
    private $redis;
    public function __construct(Redis $redis) {
        $this->redis = $redis;
    }
    /**
     * 缓存热门feed列表
     */
    public function cacheHotFeeds(array $feeds, int $ttl = 300): void {
        $key = 'feed:hot:' . date('YmdH');
        $this->redis->setex($key, $ttl, json_encode($feeds));
    }
    /**
     * 增量更新排序(新feed插入时维护排序列表)
     */
    public function addFeedToSortedSet(string $feedId, float $score): void {
        $key = 'feed:scores:day:' . date('Ymd');
        $this->redis->zadd($key, $score, $feedId);
        // 保留前1000条
        $this->redis->zremrangebyrank($key, 0, -1001);
    }
    /**
     * 获取排序后的feed ID列表
     */
    public function getTopFeeds(int $limit = 20): array {
        $key = 'feed:scores:day:' . date('Ymd');
        return $this->redis->zrevrange($key, 0, $limit - 1);
    }
}
?>

完整示例

<?php
// 集成示例
class FeedService {
    private $ranker;
    private $cache;
    public function __construct() {
        // 配置权重
        $this->ranker = new ComprehensiveRanker();
        $this->ranker->setWeights([
            'time' => 0.35,
            'likes' => 0.25,
            'comments' => 0.25,
            'shares' => 0.15
        ]);
    }
    public function getFeedList(int $userId, int $page = 1, int $limit = 20): array {
        // 获取原始feed
        $feeds = $this->fetchFeedsFromDB($page, $limit);
        // 个性化排序
        $userInterests = $this->getUserInterests($userId);
        $userHistory = $this->getUserHistory($userId);
        $personalRanker = new PersonalizedRanker($userInterests, $userHistory);
        foreach ($feeds as &$feed) {
            // 综合排序 + 个性化加权
            $feed['score'] = $this->ranker->calculateScore($feed) * 0.7 + 
                           $personalRanker->personalizedScore($feed) * 0.3;
        }
        // 排序和分页
        usort($feeds, function($a, $b) {
            if ($b['score'] == $a['score']) {
                return strtotime($b['created_at']) <=> strtotime($a['created_at']);
            }
            return $b['score'] <=> $a['score'];
        });
        return array_slice($feeds, ($page - 1) * $limit, $limit);
    }
}
?>

算法选择建议

  1. 小型社区:简单时间排序足够型平台**:Reddit/Hacker News算法较合适
  2. 社交平台:需要个性化排序
  3. 商业场景:综合权重算法更佳

选择时要考虑业务目标、用户规模、内容特性和性能要求。

抱歉,评论功能暂时关闭!