Finding all non-conflicting combinations of values from multiple lists of values

前端 未结 9 1026
没有蜡笔的小新
没有蜡笔的小新 2021-01-01 05:34

I have the following array which contains arrays of values:

$array = array(
    array(\'1\', \'2\'),
    array(\'a\', \'b\', \'c\'),
    array(\'x\', \'y\'),         


        
相关标签:
9条回答
  • 2021-01-01 06:17

    Interesting problem! This turned out to be more complex than I thought, but it seems to work.

    Basic strategy is to first order the arrays smallest to largest (keeping track of what order they were in, so I can output the answers in the correct order).

    I keep answers in the form of an array of indexes into this sorted array of input lists.

    Now that the lists are sorted, I can store the first correct answer as array(0,1,2,...,n);

    Then I recurse into a function for trying all values in the first slot there (the 0 above) by swapping it with other values in that answer array (all the ones that aren't too big for that slot). Since I've got it sorted by size, I can move any value to the right when I'm swapping, without worrying about it being to big for that right slot.

    outputting each valid slot has some crazy indirection to undo all the sorting.

    Sorry if this looks confusing. I didn't spend much time cleaning it up.

    <?php
    # $lists is an array of arrays
    function noconfcombos($lists) {
        $lengths = array();
        foreach($lists as $list) {
            $lengths[] = count($list);
        }
    
        # find one solution (and make sure there is one)
        $answer = array();
        $sorted_lengths = $lengths;
        asort($sorted_lengths);
        $answer_order_lists = array();
        $answer_order_lengths = array();
        $output_order = array();
        $min = 1;
        $max_list_length = 0;
        foreach($sorted_lengths as $lists_key => $list_max) {
            if($list_max < $min) {
                # no possible combos
                return array();
            }
            $answer[] = $min - 1; # min-1 is lowest possible value (handing out colums starting with smallest rows)
            $output_order[$lists_key] = $min - 1; # min-1 is which slot in $answers corresponds to this list
            $answer_order_lists[] = $lists[$lists_key];
            $answer_order_lengths[] = $lengths[$lists_key];
            ++$min;
        }
        ksort($output_order);
        $number_of_lists = count($lists);
        $max_list_length = end($sorted_lengths);
        if($max_list_length > $number_of_lists) {
           for($i = $number_of_lists; $i < $max_list_length; ++$i) {
              $answer[] = $i;
           }
           $stop_at = $number_of_lists;
        } else {
           $stop_at = $number_of_lists - 1;
        }
    
        # now $answer is valid (it has the keys into the arrays in $list for the
        # answer), and we can find the others by swapping around the values in
        # $answer.
    
        $ret = array();
        $ret[] = noconfcombos_convert($answer, $answer_order_lists, $output_order);
        noconfcombos_recurse($ret, $max_list_length, $stop_at, $answer_order_lengths, $answer_order_lists, $output_order, $answer, 0);
    
        return $ret;
    }
    
    # try swapping in different indexes into position $index, from the positions
    # higher, then recurse
    function noconfcombos_recurse(&$ret, $max_list_length, $stop_at, &$lengths, &$lists, &$output_order, $answer, $index) {
        if($index < $stop_at) {
            noconfcombos_recurse($ret, $max_list_length, $stop_at, $lengths, $lists, $output_order, $answer, $index + 1);
        }
        for($other = $index + 1; $other < $max_list_length; ++$other) {
            if($answer[$other] < $lengths[$index]) { # && $answer[$index] < $lengths[$other]) {
                $tmp = $answer[$index];
                $answer[$index] = $answer[$other];
                $answer[$other] = $tmp;
                $ret[] = noconfcombos_convert($answer, $lists, $output_order);
                if($index < $stop_at) {
                    noconfcombos_recurse($ret, $max_list_length, $stop_at, $lengths, $lists, $output_order, $answer, $index + 1);
                }
            }
        }
    }
    
    
    function noconfcombos_convert(&$indexes, &$lists, &$order) {
        $ret = '';
        foreach($order as $i) {
            $ret .= $lists[$i][$indexes[$i]];
        }
        return $ret;
    }
    
    function noconfcombos_test() {
        $a = array('1', '2', '3', '4');
        $b = array('a', 'b', 'c', 'd', 'e');
        $c = array('x', 'y', 'z');
        $all = array($a, $b, $c);
        print_r(noconfcombos($all));
    }
    
    noconfcombos_test();
    
    0 讨论(0)
  • 2021-01-01 06:18

    This can be refactored using recursion making it work with any arbitrary amount of arrays. If I find the time, I'll give it a try myself.

    ps. I don't know php, the example is written in Delphi.

    Edit: recursive solution with arbitrary # arrays

    type
      TSingleArray = array of string;
      TMasterArray = array of TSingleArray;
    var
      solutions: array of integer; // Q&D container to hold currently used indexes of SingleArrays
    
    
    procedure WriteSolution(const masterArray: TMasterArray);
    var
      I: Integer;
      indexes: string;
      solution: string;
    begin
      for I := 0 to High(solutions) do
      begin
        indexes := indexes + IntToStr(solutions[I]) + ' ';
        solution := solution + masterArray[I][solutions[I]];
      end;
      Writeln('Solution: ' + solution + ' Using indexes: ' + indexes);
    end;
    
    procedure FindSolution(const masterArray: TMasterArray; const singleArrayIndex: Integer; var bits: Integer);
    var
      I: Integer;
    begin
      for I := 0 to High(masterArray[singleArrayIndex]) do
      begin
        //***** Use bit manipulation to check if current index is already in use
        if bits and (1 shl I)  = (1 shl I ) then continue;
        solutions[singleArrayIndex] := I;
        Inc(bits, 1 shl I);
        //***** If it is not the last array in our masterArray, continue by calling RecursArrays recursivly.
        if singleArrayIndex <> High(masterArray) then FindSolution(masterArray, Succ(singleArrayIndex), bits)
        else WriteSolution(masterArray);
        Dec(bits, 1 shl I);
      end;
    end;
    
    //***************
    // Initialization
    //***************
    var
      I, J: Integer;
      bits: Integer;
      singleArrayString: string;
      masterArray: TMasterArray;
    begin
      I := 10;
      SetLength(masterArray, I);
      for I := 0 to High(masterArray) do
      begin
        SetLength(masterArray[I], High(masterArray) + Succ(I));
        singleArrayString := EmptyStr;
        for J := 0 to High(masterArray[I]) do
        begin
          masterArray[I][J] := IntToStr(J);
          singleArrayString := singleArrayString + masterArray[I][J];
        end;
        WriteLn(singleArrayString)
      end;
      ReadLn;
    
      //****** Start solving the problem using recursion
      bits := 0;
      SetLength(solutions, Succ(High(masterArray)));
      FindSolution(masterArray, 0, bits);    
    end.
    
    0 讨论(0)
  • 2021-01-01 06:18

    Another option:

        $arr = array(
            array('1', '2'),
            array('a', 'b', 'c'),
            array('x', 'y'),
        );
        //-----
        //assuming $arr consists of non empty sub-arrays
        function array_combinations($arr){ 
            $max = 1;
            for ($i = 0; $i < count($arr); $i ++){
                $max *= count($arr[$i]); 
            }
            $matrix = array();
            for ($i = 0; $i < $max; $i ++){
                $matrix = array(); 
            }
            $c_rep = 1;
            for ($i = count($arr) - 1; $i >= 0; $i --){
                $c_rep *= ($i < count($arr) - 1)//last sub-array 
                    ? count($arr[$i + 1])
                    : 1;
                $k = 0; 
                while ($k < $max){
                    for ($t = 0; $t < count($arr[$i]); $t ++){
                        for ($j = 0; $j < $c_rep; $j ++){
                            $matrix[$i][$k ++] = $arr[$i][$t];
                        }
                    }   
                }
            }
            return $matrix;
        }
        //-----
        $matrix = array_combinations($arr);
    
    0 讨论(0)
提交回复
热议问题