Get possible array combinations

后端 未结 3 2049
悲&欢浪女
悲&欢浪女 2021-01-01 00:05

SO,

The problem

From SQL I\'m getting an array with strings (flat array) - let it be

$rgData = [\'foo\', \'bar\', \'baz\', \'bee\         


        
相关标签:
3条回答
  • 2021-01-01 00:21

    Here is a recursive solution:

    function subcombi($arr, $arr_size, $count)
    {
       $combi_arr = array();
       if ($count > 1) {
          for ($i = $count - 1; $i < $arr_size; $i++) {
             $highest_index_elem_arr = array($i => $arr[$i]);
             foreach (subcombi($arr, $i, $count - 1) as $subcombi_arr) {
                $combi_arr[] = $subcombi_arr + $highest_index_elem_arr;
             }
          }
       } else {
          for ($i = $count - 1; $i < $arr_size; $i++) {
             $combi_arr[] = array($i => $arr[$i]);
          }
       }
       return $combi_arr;
    }
    
    function combinations($arr, $count)
    {
       if ( !(0 <= $count && $count <= count($arr))) {
          return false;
       }
       return $count ? subcombi($arr, count($arr), $count) : array();
    }    
    
    $input_arr = array('foo', 'bar', 'baz', 'bee', 'feo');
    $combi_arr = combinations($input_arr, 3);
    var_export($combi_arr); echo ";\n";
    
    OUTPUT:
    
    array (
      0 => 
      array (
        0 => 'foo',
        1 => 'bar',
        2 => 'baz',
      ),
      1 => 
      array (
        0 => 'foo',
        1 => 'bar',
        3 => 'bee',
      ),
      2 => 
      array (
        0 => 'foo',
        2 => 'baz',
        3 => 'bee',
      ),
      3 => 
      array (
        1 => 'bar',
        2 => 'baz',
        3 => 'bee',
      ),
      4 => 
      array (
        0 => 'foo',
        1 => 'bar',
        4 => 'feo',
      ),
      5 => 
      array (
        0 => 'foo',
        2 => 'baz',
        4 => 'feo',
      ),
      6 => 
      array (
        1 => 'bar',
        2 => 'baz',
        4 => 'feo',
      ),
      7 => 
      array (
        0 => 'foo',
        3 => 'bee',
        4 => 'feo',
      ),
      8 => 
      array (
        1 => 'bar',
        3 => 'bee',
        4 => 'feo',
      ),
      9 => 
      array (
        2 => 'baz',
        3 => 'bee',
        4 => 'feo',
      ),
    );
    

    The recursion is based on the fact that to get all combinations of k ($count) elements out of n ($arr_size) you must, for all possible choices of the highest zero-based index i, find all "subcombinations" of k-1 elements out of the remaining i elements with index lower than i.

    The array is not array_sliced when it's passed to the recursive calls in order to take advantage of PHP's "lazy copy" mechanism. This way no real copying takes place, since the array is not modified.

    Conserving array indices is nice for debugging purposes, but it's not necessary. Surprisingly, simply removing the $i => parts and replacing the array + with an array_merge causes a considerable slowdown. To attain a slightly better speed than the original version, you have to do this:

    function subcombi($arr, $arr_size, $count)
    {
       $combi_arr = array();
       if ($count > 1) {
          for ($i = $count - 1; $i < $arr_size; $i++) {
             $highest_index_elem = $arr[$i];
             foreach (subcombi($arr, $i, $count - 1) as $subcombi_arr) {
                $subcombi_arr[] = $highest_index_elem;
                $combi_arr[] = $subcombi_arr;
             }
          }
       } else {
          for ($i = $count - 1; $i < $arr_size; $i++) {
             $combi_arr[] = array($arr[$i]);
          }
       }
       return $combi_arr;
    }
    


    Regarding the first part of your question, you should avoid calculating the same quantity more than once, and you should minimize function calls. E.g., like this:

    function nextAssoc($sAssoc)
    {
       if (false !== ($iPos = strrpos($sAssoc, '01')))
       {
          $sAssoc[$iPos]   = '1';
          $sAssoc[$iPos+1] = '0';
          $tailPos = $iPos+2;
          $n0 = substr_count($sAssoc, '0', $tailPos);
          $n1 = strlen($sAssoc) - $tailPos - $n0;
          return substr($sAssoc, 0, $tailPos).str_repeat('0', $n0)
                                             .str_repeat('1', $n1);
       }
       return false;
    }
    

    It's hard to do deeper changes to your code without turning it inside out. It's not too bad though, since in my tests its speed is approximately half the one of my recursive solution (i.e., times are ca. double)

    0 讨论(0)
  • 2021-01-01 00:25

    I've just tried to solve this problem with minimum time complexity and without using recursion using go language.

    I've seen a few solutions, but with using a recursive function. Avoiding recursion to solve stack size exceeded error.

    package main
    
    import "fmt"
    
    func main() {
        // Arguments
        arr := []string{"foo", "bar", "baz", "bee", "feo", "boo", "bak"}
        combinations := make([][]string, 0)
        k := 4
        n := len(arr)
    
        // Execution starts from here
        if k > n {
            panic("invalid requirement")
        }
    
        pos := make([]int, k) // this variable is used to plot the unique combination of elements
    
        // initialize an array with first ever plotting possitions
        i := 0
        c := k
        for c > 0 {
            c--
            pos[i] = c
            i++
        }
        combinations = append(combinations, getCombination(arr, pos, k))
    
        // Let's begin the work
        x := 0
        ctr := 1 // counter is use to calculate total iterations
        for pos[x] < n-(x+1) {
            ctr++
            pos[x]++
    
            combinations = append(combinations, getCombination(arr, pos, k))
    
            if pos[x] == n-(x+1) && x+1 < k {
                x++
                i := x
                s := pos[x] + 1
                for i > 0 {
                    i--
                    s++
                    pos[i] = s
                }
    
                // continue to next index
                continue
            }
    
            x = 0
    
        }
    
        fmt.Println("total # iterations: --> ", ctr)
    
        fmt.Println(combinations, "\ntotal # combinations: ", len(combinations))
    
    }
    
    func getCombination(arr []string, pos []int, k int) []string {
        combination := make([]string, k)
        for i, j := k-1, 0; i >= 0; i, j = i-1, j+1 {
            combination[j] = arr[pos[i]]
        }
        return combination
    }
    

    The working example is here https://play.golang.org/p/D6I5aq8685-

    0 讨论(0)
  • 2021-01-01 00:33

    I'm sorry for not providing a PHP solution, because I didn't program in PHP for quite a long time now, but let me show you a quick Scala solution. Maybe it will inspire you:

    val array = Vector("foo", "bar", "baz", "bee", "feo")
    for (i <- 0 until array.size; 
         j <- i + 1 until array.size; 
         k <- j + 1 until array.size)      
        yield (array(i), array(j), array(k))
    

    Result:

    Vector((foo,bar,baz), (foo,bar,bee), (foo,bar,feo), (foo,baz,bee), (foo,baz,feo), (foo,bee,feo), (bar,baz,bee), (bar,baz,feo), (bar,bee,feo), (baz,bee,feo))
    

    Universal code for generating k-combinations:

    def combinations(array: Vector[String], k: Int, start: Int = 0): Iterable[List[String]] = { 
      if (k == 1 || start == array.length) 
        for (i <- start until array.length) yield List(array(i))
      else 
        for (i <- start until array.length; c <- combinations(array, k - 1, i + 1)) yield array(i) :: c 
    }
    

    Results:

    scala> combinations(Vector("a", "b", "c", "d", "e"), 1)
    res8: Iterable[List[String]] = Vector(List(a), List(b), List(c), List(d), List(e))
    
    scala> combinations(Vector("a", "b", "c", "d", "e"), 2)
    res9: Iterable[List[String]] = Vector(List(a, b), List(a, c), List(a, d), List(a, e), List(b, c), List(b, d), List(b, e), List(c, d), List(c, e), List(d, e))
    
    scala> combinations(Vector("a", "b", "c", "d", "e"), 3)
    res10: Iterable[List[String]] = Vector(List(a, b, c), List(a, b, d), List(a, b, e), List(a, c, d), List(a, c, e), List(a, d, e), List(b, c, d), List(b, c, e), List(b, d, e), List(c, d, e))
    
    scala> combinations(Vector("a", "b", "c", "d", "e"), 4)
    res11: Iterable[List[String]] = Vector(List(a, b, c, d), List(a, b, c, e), List(a, b, d, e), List(a, c, d, e), List(b, c, d, e))
    
    scala> combinations(Vector("a", "b", "c", "d", "e"), 5)
    res12: Iterable[List[String]] = Vector(List(a, b, c, d, e))
    

    Of course, real scala code should be much more generic with regard to accepted type of elements and type of collections, but I just wanted to show the basic idea, not the most beautiful Scala code possible.

    0 讨论(0)
提交回复
热议问题