有效地保持PHP中的前5个值

时间:2009-03-21 11:57:35

标签: php algorithm optimization sorting linear

我正在用PHP写一个小算法,通过 n 数量的电影评级,并存储前5个。我不是从数据文件中读取,而是从流中读取我不能简单地通过评级来订购电影。

我的问题是,当我阅读流时,跟踪前5部评级电影最有效的方法是什么?目前我做以下事情:

  1. 读入5部电影(进入一个名为电影[]的数组),有两部电影[] [姓名]和电影[] [评级]
  2. 使用array_multisort()按电影[评级]订购数组(最高评级现在位于电影[4])
  3. 继续阅读下一部电影
  4. 如果这部新电影评分>电影[0] [评级]然后用这部新电影替换电影[0]
  5. 重新排序列表
  6. 重复3-5直到完成
  7. 我的方法有效,但每次阅读后都需要对列表进行排序。我认为这是一种昂贵的方法,主要是因为我每次使用array_multisort()时都必须对5部电影进行for循环,以构建索引进行排序。任何人都可以建议更好的方法来解决这个问题吗?

8 个答案:

答案 0 :(得分:4)

链接列表可以在这里使用。

构建一个链接列表,以正确的顺序链接前5部电影。对于每部新电影,只需从链的末尾开始,直到电影介于评分较高且评分较低的电影之间。然后将链接插入此处的列表中。如果电影好于最差(因此你的列表现在是6长),只需删除链中的最后一个链接,然后你又回到5。

没有排序,没有索引。

答案 1 :(得分:3)

你的算法看起来很好。我不确定如何在PHP中实现数组。从算法的角度来看:使用堆而不是数组。

答案 2 :(得分:3)

每次阅读后重新排序都没有意义,因为你真的只需要插入一个新条目。使用以下算法,它可能会让您获得最佳速度。它基本上是一个展开的循环,而不是最漂亮的代码。

set movies[0..4].rating to -1.
while more movies in stream:
    read in next movie.
    if movie.rating < movies[0].rating:
        next while
    if movie.rating < movies[1].rating:
        movies[0] = movie
        next while
    if movie.rating < movies[2].rating:
        movies[0] = movies[1]
        movies[1] = movie
        next while
    if movie.rating < movies[3].rating:
        movies[0] = movies[1]
        movies[1] = movies[2]
        movies[2] = movie
        next while
    if movie.rating < movies[4].rating:
        movies[0] = movies[1]
        movies[1] = movies[2]
        movies[2] = movies[3]
        movies[3] = movie
        next while
    movies[0] = movies[1]
    movies[1] = movies[2]
    movies[2] = movies[3]
    movies[3] = movies[4]
    movies[4] = movie

最后,您有已分类的电影列表。如果小于5,则其他人的评分为-1,因此您将知道它们无效。这假设真实电影的评级为零或更高,但如果不是,则可以调整这些值。

如果你需要为超过5部电影调整它,你可以。最好的选择是再次卷起循环。但是,在某些时候,对它进行排序比使用这种方法更有效。这种方法仅适用于小型数据集。

答案 3 :(得分:1)

  

我的方法有效,但每次阅读后都需要对列表进行排序。

不,它没有,它只需要你找到一个评级为&gt;的新电影后排序。动画[0] [评分]。

这种方法对我来说似乎很有效。你只会偶然排序前5名的新参赛作品,你所处理的电影数量会减少。

答案 4 :(得分:0)

名单有多大?我猜这不是一个选项,可以将整个列表保存在内存中,并在最后对其进行排序?

答案 5 :(得分:0)

  1. 阵列中不需要两个键。名称为键的数组,以及值的评级。使用arsort();
  2. 对其进行排序
  3. 算法并不完美,你可以用链表进行最优化。虽然我认为在PHP中实现的链表实际上会慢于6个元素的函数调用asort()。对于大O估计,您可以假设排序6个元素具有恒定时间。
  4. 只有当您遇到的电影评级高于实际电影时才会排序,因此在平均情况下,您可以在不断增加的情况下进行评分。只有在最差情况下才能对每部电影进行排序,从最低评级中排序初始列表。

答案 6 :(得分:0)

这就是我要做的事情:

// let’s say get_next_movie () returns array with 'rating' and 'name' keys

while ($m = get_next_movie ()) {

  $ratings[$m['rating']][] = $m['movie'];

  $temp_ratings = $ratings;
  $top5 = array ();
  $rating = 5;
  while (1) {
    if (count ($temp_ratings[$rating])) {
      $top5[] = array_shift ($temp_ratings[$rating]);
    } elseif ($rating > 0) {
      --$rating;
    } else {
      break;
    }
  }

  // $top5 has current top 5 :-)

}

$ ratings数组看起来像这样,每个评级都包含一系列电影:

Array
    (
    [5] => Array
        (
            [0] => Five!
        )

    [3] => Array
        (
            [0] => Three
            [1] => Threeeeee
            [2] => Thr-eee-eee
        )

    [4] => Array
        (
            [0] => FOR
        )
    )

答案 7 :(得分:0)

也许这会有所帮助。

class TopList {
    private $items = array();
    private $indexes = array();
    private $count = 0;
    private $total = 5;
    private $lowest;
    private $sorted = false;

    public function __construct($total = null) {
        if (is_int($total))
            $this->total = $total;

        $this->lowest = -1 * (PHP_INT_MAX - 1);
    }

    public function addItem($index, $item) {
        if ($index <= $this->lowest)
            return;

        $setLowest = $this->count === $this->total;
        if ($setLowest) {
            /* //remove first added
            $lowestIndex = array_search($this->lowest, $this->indexes);
            /*/ //remove last added
            $lowestIndex = end(array_keys($this->indexes, $this->lowest));
            //*/
            unset($this->indexes[$lowestIndex], $this->items[$lowestIndex]);
        } else {
            ++$this->count;
            $setLowest = $this->count === $this->total;
        }

        $this->indexes[] = $index;
        $this->items[] = $item;
        $this->sorted = false;

        if ($setLowest)
            $this->lowest = min($this->indexes);
    }

    public function getItems() {
        if (!$this->sorted) {
            array_multisort($this->indexes, SORT_DESC, $this->items);
            $this->sorted = true;
        }
        return $this->items;
    }
}

$top5 = new TopList(5);
foreach ($movies as $movie) {
    $top5->addItem($movie['rating'], $movie);
}
var_dump($top5->getItems());