【问题标题】:PHP find all combinations to a sum in inner arrayPHP在内部数组中找到所有组合的总和
【发布时间】:2017-09-04 15:55:55
【问题描述】:

我正在为酒店的可用房间编写 PHP 脚本。我想要一组(即 4 人)的每个组合。这是我的数组。

$room_array = array(
    array(
        "title"         => "1 person room",
        "room_for"      => 1,
        "price"         => 79
    ),
    array(
        "title"         => "2 person room with other",
        "room_for"      => 1,
        "price"         => 69
    ),
    array(
        "title"         => "2 person room alone",
        "room_for"      => 1, 
        "price"         => 89
    ),
    array(
        "title"         => "2 person",
        "room_for"      => 2,
        "price"         => 69
    ),
    array(
        "title"         => "3 person",
        "room_for"      => 3,
        "price"         => 69
    )
);

可能的结果:

  • 4x 1 人间
  • 4x 2 人间与其他人
  • 3x 1 人房间 + 1x 2 人房间与其他
  • 2x 2 人间
  • 1x 3 人间 + 1x 1 人间

等等。等等

这需要一个递归函数。但是我查看的每个示例都不适用于内部数组中的计数。我发现最接近的是这个问题:

Finding potential combinations of numbers for a sum (given a number set to select from)

但我没有得到解决方案来工作..

更新:

您好,感谢您的所有回答。真的帮助我找到了最佳实践。与此同时,作业发生了一些变化,所以我无法回答自己最初的问题。我的问题解决了。再次感谢您的帮助!

【问题讨论】:

  • 如果你摆脱数组结构并寻找对象或节点,你可以使用 Dijkstra 的算法并稍微改变一下以满足你的需要。
  • 你所说的“与其他人”是什么意思?只要每个人都有一个房间,总数实际上可以超过四个?
  • 使用this 答案获取所有组合,更改功能以返回数组键而不是客人数量。
  • @Don'tPanic “与其他人”意味着您与“陌生人”共享房间。你不知道你的室友是谁。
  • 我明白了。感谢您的澄清。

标签: php multidimensional-array


【解决方案1】:

我在下面的回答会让你成功。


资源

我从this 的答案中借用了一些代码逻辑。 要引用答案(以防将来被删除),请在下面查看。

你可以试试

echo "<pre>";
$sum = 12 ; //SUM
$array = array(6,1,3,11,2,5,12);
$list = array();
# Extract All Unique Conbinations
extractList($array, $list);
#Filter By SUM = $sum     $list =
array_filter($list,function($var) use ($sum) { return(array_sum($var) == $sum);});
#Return Output
var_dump($list);

输出

array
  0 => array
    1 => string '1' (length=1)
    2 => string '2' (length=1)
    3 => string '3' (length=1)
    4 => string '6' (length=1)
  1 => array
    1 => string '1' (length=1)
    2 => string '5' (length=1)
    3 => string '6' (length=1)
  2 => array
    1 => string '1' (length=1)
    2 => string '11' (length=2)
  3 => array
    1 => string '12' (length=2)

使用的功能

function extractList($array, &$list, $temp = array()) {
    if(count($temp) > 0 && ! in_array($temp, $list))
        $list[] = $temp;
    for($i = 0; $i < sizeof($array); $i ++) {
        $copy = $array;
        $elem = array_splice($copy, $i, 1);
        if (sizeof($copy) > 0) {
            $add = array_merge($temp, array($elem[0]));
            sort($add);
            extractList($copy, $list, $add);
        } else {
            $add = array_merge($temp, array($elem[0]));
            sort($add);
            if (! in_array($temp, $list)) {
                $list[] = $add;
            }
        }
    }
}

我的回答

下面的代码使用上面引用的代码。我更改了 array_filter 函数的返回功能以将其映射到您的需求。

您唯一要做的就是更改函数,以便它可以捕获多个相同类型的房间。目前,下面的代码只会输出每种房间类型的 1 个(根据上面引用的代码)。解决此问题的一种简单方法是将您发送给函数的数组值乘以您正在搜索房间的客人数量,但最多为可用房间数量。所以:如果您要为 4 位客人预订,并且您没有剩余单人房且只有 1 间双人房,那么您的最佳匹配结果将必须是 2 person room3 person room。我已经添加了一些 brief 功能来添加它(它已被注释掉),尽管我还没有对其进行测试。处理它也可能需要一段时间,因此如果您正在寻找更快的方法,您将不得不使用之前 cmets/answers 中已经提到的更好的算法或解决 P vs NP

下面的代码还为您提供了切换$exact 值的选项。如果设置为true,此值将仅返回完全等于客人数量的匹配项,如果设置为false,将返回至少等于客人数量的所有匹配项客人

<?php

class Booking {
    private $minGuests = 1;
    protected $guests = 1;
    protected $rooms = [];

    public function getRoomCombinations(bool $exact = true) {
        $guests = $this->guests;
        $list = [];
        $rooms = $this->rooms;

        /*for($i = 0; $i < $guests-1; $i++) {
            $rooms = array_merge($rooms, $this->rooms);
        }
        asort($rooms);*/
        $this->extractList($rooms, $list);

        $result = array_filter($list, function($var) use ($guests, $exact) {
            if($exact)
                return(array_sum(array_map(function($item) { return $item['room_for'];}, $var)) == $guests);
            else
                return(array_sum(array_map(function($item) { return $item['room_for'];}, $var)) >= $guests && count($var) <= $guests);
        });
        array_multisort(array_map('count', $result), SORT_ASC, $result);

        return $result;
    }

    private function extractList(array $array, array &$list, array $temp = []) {
        if (count($temp) > 0 && !in_array($temp, $list))
            $list[] = $temp;
        for($i = 0; $i < sizeof($array); $i++) {
            $copy = $array;
            $elem = array_splice($copy, $i, 1);
            if (sizeof($copy) > 0) {
                $add = array_merge($temp, array($elem[0]));
                sort($add);
                $this->extractList($copy, $list, $add);
            } else {
                $add = array_merge($temp, array($elem[0]));
                sort($add);
                if (!in_array($temp, $list)) {
                    $list[] = $add;
                }
            }
        }
    }

    public function setGuests(int $guests) {
        $this->guests = ($guests >= $this->minGuests ? $guests : $this->minGuests);
        return $this;
    }

    public function setHotelRooms(array $rooms) {
        $this->rooms = $rooms;
        return $this;
    }
}

$booking = (new Booking())
    ->setGuests(4)
    ->setHotelRooms([
        [
            "title"         => "1 person room",
            "room_for"      => 1,
            "price"         => 79
        ],
        [
            "title"         => "2 person room with other",
            "room_for"      => 1,
            "price"         => 69
        ],
        [
            "title"         => "2 person room alone",
            "room_for"      => 1, 
            "price"         => 89
        ],
        [
            "title"         => "2 person",
            "room_for"      => 2,
            "price"         => 69
        ],
        [
            "title"         => "3 person",
            "room_for"      => 3,
            "price"         => 69
        ]
    ]);
echo '<pre>' . var_export($booking->getRoomCombinations(true), true) . '</pre>';
?>

【讨论】:

    【解决方案2】:

    如果您需要所有组合,则可以使用回溯迭代算法(深度路径)。

    总结:

    1. 树的类型:二叉树,因为当contabilized的人数=目标时,所有级别都可以包含一个解决方案

      Binary tree

    2. 算法函数 每次生成关卡时都需要根据关卡人数增加cont,改变轨迹(探索兄弟或返回)时减少cont

    解决方案:array[0..levels-1] values {0 (node not selected) ,1 (node selected)}

    solution[0] = 1 -> 你选择“1 person room”属于解决方案

    解决方案:对象列表/数组,每个对象都包含房间标题数组

    function Backtracking ()
        level:= 1
        solution:= s_initial
        end:= false
        repeat
             generate(level, solution)
             IF solution(level, solution) then
               save_solution
             else if test(level, solution) then
                level:= level+ 1
             else
                while NOT MoreBrothers(level, solution)
                   go_back(level, s)
    
        until level==0
    

    2.1。生成:生成下一个节点

    2.2。解决方案:测试是否是解决方案

    2.3。标准:如果我们必须继续沿着这条轨道或边界

    2.4。 MoreBrothers:如果有节点在这个级别没有检查

    2.5。回溯:探索了该层所有节点

    2.6。保存解决方案:将包含字符串的对象添加到解决方案数组中

    【讨论】:

    • 听起来 OP 正在寻找用 PHP 编写的解决方案
    【解决方案3】:
    $room_array = array(
        array(
            "title"         => "1 person room",
            "room_for"      => 1,
            "price"         => 79
        ),
        array(
            "title"         => "2 person room with other",
            "room_for"      => 1,
            "price"         => 69
        ),
        array(
            "title"         => "2 person room alone",
            "room_for"      => 1,
            "price"         => 89
        ),
        array(
            "title"         => "2 person",
            "room_for"      => 2,
            "price"         => 69
        ),
        array(
            "title"         => "3 person",
            "room_for"      => 3,
            "price"         => 69
        )
    );
    
    // Gets rooms based on a given number of guests
    function get_possible_rooms($num_guests) {
        global $room_array;
        $possible_rooms = [];
    
        foreach ($room_array as $room) {
            if ($num_guests <= $room['room_for']) {
                $possible_rooms[] = $room['title'];
            }
        }
    
        return $possible_rooms;
    }
    
    // Gets the available room capacities
    function get_room_capacities() {
        global $room_array;
        $capacities = [];
    
        foreach ($room_array as $room) {
            $capacities[] = $room['room_for'];
        }
    
        return array_unique($capacities);
    }
    
    // Gets the different combinations of groups of guests based on the room capacities
    function get_guest_assignments($remaining_guests, $parent_id = '', $num_guests, &$result) {
        $room_capacities = get_room_capacities();
    
        for ($i = 1; $i <= $remaining_guests; ++$i) {
            if (in_array($i, $room_capacities)) {
                $parent_guests = (isset($result[$parent_id])) ? $result[$parent_id] : 0;
                $result[$parent_id . $i] = $parent_guests + $i;
    
                for ($j = 1; $j <= $remaining_guests - $i; ++$j) {
                    // Recursively get the results for children
                    get_guest_assignments($j, $parent_id . $i, $num_guests, $result);
                }
            }
        }
    
        if ($remaining_guests === 1 && $parent_id !== '') {
            // If it reaches the end and it does not fulfill the required number of guests,
            // mark it for removal later
            if ($result[$parent_id] < $num_guests) {
                $result[$parent_id] = null;
            }
        }
    
        // This is the last recursion
        if ($result[$parent_id . '1'] === $num_guests) {
            // Remove duplicates. 
            // To do this, we need to re-sort the keys (e.g. 21 becomes 12) and call array_unique()
            // I admit this is a bit sloppy implementation.
            $combinations = [];
    
            foreach ($result as $key => $value) {
                if ($value !== null) {
                    $nums = str_split($key);
                    sort($nums);
                    $combinations[] = implode('', $nums);
                }
            }
    
            $result = array_unique($combinations);
        }
    }
    
    // Gets the rooms for each group of guest
    function get_room_assignments($guest_str) {
        $rooms = [];
    
        for ($i = 0; $i < strlen($guest_str); ++$i) {
            $num_guests = intval(substr($guest_str, $i, 1));
            $rooms[] = get_possible_rooms($num_guests);
        }
    
        return $rooms;
    }
    
    //----------
    // RUN
    //----------
    
    $guests = 4;
    $result = [];
    
    get_guest_assignments($guests, null, $guests, $result);
    
    foreach ($result as $guest_combi) {
        $assignments = get_room_assignments($guest_combi);
    
        // Printing output
        echo 'Guest Combination ' . $guest_combi . "\n";
        echo json_encode($assignments, JSON_PRETTY_PRINT);
        echo "\n\n";
    }
    

    输出将如下所示:

    ...
    Guest Combination 13
    [
        [
            "1 person room",
            "2 person room with other",
            "2 person room alone",
            "2 person",
            "3 person"
        ],
        [
            "3 person"
        ]
    ]
    ...
    

    “客人组合13”是指4位客人将分成1人和3人一组。

    输出是每个组可能的房间数组。所以在示例中,1 人组 可以预订 1 人房间,2 人房间与其他人,... 3 人房间。 3人组可以预定3人间。

    其他说明:

    • 我知道我们讨厌global,但这样做只是为了简洁。随意修改。
    • 有一种更短的编码方式,但这种实现更易于调试,因为访客组合用作键。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多