在PHP中查找数组的子集

p8ekf7hl  于 2023-08-02  发布在  PHP
关注(0)|答案(5)|浏览(105)

我有一个带有属性(A B C D)的关系模式。我也有一套函数依赖关系。
现在我需要确定R属性的所有可能子集的闭包。这就是我被困住的地方。我需要学习如何在PHP中查找子集(非重复)。
我的数组是这样存储的。

$ATTRIBUTES = ('A', 'B', 'C', 'D').

字符串
所以我的子集应该

$SUBSET = ('A', 'B', 'C', 'D', 'AB', 'AC', AD', 'BC', 'BD', 'CD', 'ABC', 'ABD', 'BCD', 'ABCD')


代码不应该是什么大的,但出于某种原因,我不能让我的头周围。

zbdgwd5y

zbdgwd5y1#

使用php array_merge我们可以有一个很好的短powerSet函数

// ["A", "B", "C"]
// [[],["A"],["B"],["A","B"],["C"],["A","C"],["B","C"],["A","B","C"]]
function powerSet(array $array) : array {
    // add the empty set
    $results = [[]];

    foreach ($array as $element) {
        foreach ($results as $combination) {
            $results[] = [...$combination, $element];
        }
    }

    return $results;
}

字符串

uoifb46i

uoifb46i2#

你想要$attributes的电源组?这就是你的问题所暗示的。
一个例子可以在这里找到(引用完整)

<?php 
/** 
* Returns the power set of a one dimensional array, a 2-D array. 
* [a,b,c] -> [ [a], [b], [c], [a, b], [a, c], [b, c], [a, b, c] ]
*/ 
function powerSet($in,$minLength = 1) { 
   $count = count($in); 
   $members = pow(2,$count); 
   $return = array(); 
   for ($i = 0; $i < $members; $i++) { 
      $b = sprintf("%0".$count."b",$i); 
      $out = array(); 
      for ($j = 0; $j < $count; $j++) { 
         if ($b{$j} == '1') $out[] = $in[$j]; 
      } 
      if (count($out) >= $minLength) { 
         $return[] = $out; 
      } 
   } 
   return $return; 
}

字符串

wixjitnu

wixjitnu3#

这里有一个回溯解决方案。
给定一个函数,返回输入集的所有L长度子集,查找从L = 2到数据集输入长度的所有L长度子集

<?php

function subsets($S,$L) {
    $a = $b = 0;
    $subset = [];
    $result = [];
    while ($a < count($S)) {
        $current = $S[$a++];
        $subset[] = $current;
        if (count($subset) == $L) {
            $result[] = json_encode($subset);
            array_pop($subset);
        }
        if ($a == count($S)) {
            $a = ++$b;
            $subset = [];
        }
    }
    return $result;
}


$S = [ 'A', 'B', 'C', 'D'];
$L = 2;

// L = 1 -> no need to do anything
print_r($S);

for ($i = 2; $i <= count($S); $i++)
    print_r(subsets($S,$i));

字符串

nzk0hqpo

nzk0hqpo4#

基于@Yada的答案,这将生成数组的幂集,但保留每个子集中原始数组的键(返回值仍然是数字和顺序索引)。如果你需要一个关联数组的子集,这是非常有用的。
这些子集还保留原始数组的元素顺序。我在$results中添加了一个稳定排序,因为我需要它,但您可以省略它。

function power_set($array) {
    $results = [[]];
    foreach ($array as $key => $value) {
        foreach ($results as $combination) {
            $results[] = $combination + [$key => $value];
        }
    }

    # array_shift($results); # uncomment if you don't want the empty set in your results
    $order = array_map('count', $results);
    uksort($results, function($key_a, $key_b) use ($order) {
        $comp = $order[$key_a] - $order[$key_b]; # change only this to $order[$key_b] - $order[$key_a] for descending size
        if ($comp == 0) {
            $comp = $key_a - $key_b;
        }
        return $comp;
    });
    return array_values($results);
}

字符串
给定OP的输入,var_dump(power_set(['A', 'B', 'C', 'D']));提供:

array(16) {
  [0] =>
  array(0) {
  }
  [1] =>
  array(1) {
    [0] =>
    string(1) "A"
  }
  [2] =>
  array(1) {
    [1] =>
    string(1) "B"
  }
  [3] =>
  array(1) {
    [2] =>
    string(1) "C"
  }
  [4] =>
  array(1) {
    [3] =>
    string(1) "D"
  }
  [5] =>
  array(2) {
    [0] =>
    string(1) "A"
    [1] =>
    string(1) "B"
  }
  [6] =>
  array(2) {
    [0] =>
    string(1) "A"
    [2] =>
    string(1) "C"
  }
  [7] =>
  array(2) {
    [1] =>
    string(1) "B"
    [2] =>
    string(1) "C"
  }
  [8] =>
  array(2) {
    [0] =>
    string(1) "A"
    [3] =>
    string(1) "D"
  }
  [9] =>
  array(2) {
    [1] =>
    string(1) "B"
    [3] =>
    string(1) "D"
  }
  [10] =>
  array(2) {
    [2] =>
    string(1) "C"
    [3] =>
    string(1) "D"
  }
  [11] =>
  array(3) {
    [0] =>
    string(1) "A"
    [1] =>
    string(1) "B"
    [2] =>
    string(1) "C"
  }
  [12] =>
  array(3) {
    [0] =>
    string(1) "A"
    [1] =>
    string(1) "B"
    [3] =>
    string(1) "D"
  }
  [13] =>
  array(3) {
    [0] =>
    string(1) "A"
    [2] =>
    string(1) "C"
    [3] =>
    string(1) "D"
  }
  [14] =>
  array(3) {
    [1] =>
    string(1) "B"
    [2] =>
    string(1) "C"
    [3] =>
    string(1) "D"
  }
  [15] =>
  array(4) {
    [0] =>
    string(1) "A"
    [1] =>
    string(1) "B"
    [2] =>
    string(1) "C"
    [3] =>
    string(1) "D"
  }
}

ct3nt3jp

ct3nt3jp5#

在@fbstj回答之后,我更新了函数:

  • 使用bitwize operators而不是sprintf(@Titus评论)
  • 处理空集(@James Stott & @fbstj评论)
  • 更新语法到PHP 7+
function powerSet(array $in, int $minLength = 0): array
{
    $return = [];
    
    if ($minLength === 0) {
        $return[] = [];
    }

    for ($i = 1 << count($in); --$i;) {
        $out = [];

        foreach ($in as $j => $u) {
            if ($i >> $j & 1) {
                $out[] = $u;
            }
        }

        if (count($out) >= $minLength) {
            $return[] = $out;
        }
    }
    
    return $return;
}

字符串
由于幂集函数会增加很多内存负载(2count($in)次迭代),请考虑使用Generator

function powerSet(array $in, int $minLength = 0): \Generator
{
    if ($minLength === 0) {
        yield [];
    }

    for ($i = 1 << count($in); --$i;) {
        $out = [];

        foreach ($in as $j => $u) {
            if ($i >> $j & 1) {
                $out[] = $u;
            }
        }

        if (count($out) >= $minLength) {
            yield $out;
        }
    }
}


用途:

foreach (powerSet(range(1, 10)) as $value) {
    echo implode(', ', $value) . "\n";
}

相关问题