在不遍历整个范围的情况下查找范围之间的值


Finding value between ranges without going through the entire ranges

我想根据我的玩家有多少朋友来给他们奖金。

我有断点(例如0、1、5、10、25)

对于0个朋友,他将获得0奖励。

对于1个朋友,他可以得到1000,对于5个或以上的2000等等…

我现在做的是:

public function getFriendsBonusByFriendsAmount($amount)
{
    switch (true) {
        case ($amount < 1):
            return 0;
        case ($amount < 5):
            return 1000;
        case ($amount < 10):
            return 2000;
        case ($amount < 25):
            return 3000;
        case ($amount >= 25):
            return 5000;
    }
}

我正在寻找一种不同的方式来寻找奖金,而无需foreach/switch

也许想想我可以玩的一个或多个数组?

$bonusBreakpoints = [
    0 => 0,
    1 => 1000,
    5 => 2000,
    10 => 3000,
    25 => 5000
]

或者可能是两个具有各自索引的数组?

我想了一种方法,但这是浪费内存:

$bonusPerFriends = [
    0 => 0,
    1 => 1000,
    2 => 1000,
    3 => 1000,
    4 => 1000,
    5 => 2000,
    6 => 2000,
    ...
    25 => 5000
]

我宁愿不用那种方法。

好吧,有时foreach/switch将是最好的解决方案:)

/**
 * Calculates bonus based on how many 
 * friends the player have from predefined breakpoints 
 **/
function getBonus($friends) {
    $bonuses = [0, 1000, 2000, 3000, 5000];
    $stops   = [[PHP_INT_MIN, 0], [1, 4], [5, 14], [15, 24], [25, PHP_INT_MAX]];
    // replace the stops by bonus if match, otherwise return empty - O(n)
    $bonus = array_map(function ($stop, $bonus) use ($friends) { 
        if ($friends >= $stop[0] && $friends <= $stop[1]) { 
            return $bonus;
        } 
    }, $stops, $bonuses);
    // clean up the array from empty values - O(n)
    $bonus = array_filter($bonus , 'is_numeric');
    // from array(3 => 3000) to 3000 - O(1)
    return array_pop($bonus); 
}

结果:

echo getBonus(0).PHP_EOL;  // 0
echo getBonus(4).PHP_EOL;  // 1000
echo getBonus(12).PHP_EOL; // 2000
echo getBonus(20).PHP_EOL; // 3000
echo getBonus(39).PHP_EOL; // 5000

此处的p.S.$bonuses$stops的长度必须相等。

@andrey mischenko的答案在技术上是正确的,但正如你在问题中所说,如果没有foreach,就无法解决问题 (编辑:答案已删除)试试这个:

$bonusBreakpoints = [
    0 => 0,
    1 => 1000,
    5 => 2000,
    10 => 3000,
    25 => 5000
];
$justTheKeys = array_keys($bonusBreakpoints);    
public function getFriendsBonusByFriendsAmount($amount)
{
    $bonus = array_reduce($justTheKeys, function($carryOver, $item) use ($amount)
        {
            if ($amount >= $item) return $bonusBreakpoints($item);
            return $carryOver;
        }
    return $bonus;
}

(我知道这不是array_reduce最初的目的。我把这个问题理解为一个心理游戏。比如"除了明显的方法,比如循环或开关,找到创造性的方法来解决这个问题。"如果我必须为工作编写代码,我可能也会使用循环。:)

在阅读了答案和更多研究后,我得出结论,二进制搜索是我的最佳选择。

数据看起来应该有点像这样:

$bonuses = [
    [ 'min' => 0, 'max' => 0, 'amount' => 0 ]
    [ 'min' => 1, 'max' => 4, 'amount' => 1000 ]
    [ 'min' => 5, 'max' => 14, 'amount' => 2000 ]
    ...
    [ 'min' => 25, 'max' => PHP_INT_MAX, 'amount' => 5000 ]
]

你从count($bonuses)/2开始,从那里检查你是否低于最小值,是这样的,走到一半,否则检查你是否高于最大值,然后走到一半。其他的只要退还奖金,因为你在正确的范围内。

由于我的大多数用户没有朋友或超过25个,我可能会先检查第一个和最后一个单元格。