2016-12-06 14 views
1

における閾値と配列の共通項を計算します。のは、私は以下の配列を持っていると言うPHP

$a = [1,2,3,4,5]; 
$b = [1,3,4,5,6]; 
$c = [1,7,8,9,10]; 
$d = [1,2,3,4]; 

それらの交点が十分に簡単です$result = [1]、だろう。しかし、私は3のような最小のしきい値を持つそれらの交差を望んでいたらどうですか?

$result = [1,3,4]; 

1、3及び4 $で存在する:閾値は、私は限り私得交差点は、この場合に生じる可能性があり、少なくとも3つの要素を有するように、交差点から1つ以上のアレイをスキップできることを意味しますa、$ b、および$ dが含まれますが、しきい値のためにスキップされる$ cには含まれません。既存のPHPクラス、アルゴリズム、関数がありますか?

+0

ビルドイン機能 - いいえ。ここにビットを書く必要があります:) – vuryss

+0

配列のサイズは?彼らは重複を持っていますか?いくつのアレイがありますか?基本的に値を数え、> 3の数を選択する必要があります。 – teran

+0

なぜ '$ c'は3のしきい値でスキップされるべきですか? – Federkun

答えて

1

これを行うには、配列の組み合わせを使用する必要があります。私はこのgreat articleの組み合わせアルゴリズムを使用しています。このアルゴリズムを調整すると、次のクラスを書くことができます:

class Intersections 
{ 
    protected $arrays; 
    private $arraysSize; 

    public function __construct($arrays) 
    { 
     $this->arrays = $arrays; 
     $this->arraysSize = count($arrays); 
    } 

    public function getByThreshold($threshold) 
    { 
     $intersections = $this->getAll(); 

     foreach ($intersections as $intersection) { 
      if (count($intersection) >= $threshold) { 
       return $intersection; 
      } 
     } 

     return null; 
    } 

    protected $intersections; 
    public function getAll() 
    { 
     if (is_null($this->intersections)) { 
      $this->generateIntersections(); 
     } 

     return $this->intersections; 
    } 


    private function generateIntersections() 
    { 
     $this->generateCombinationsMasks(); 
     $this->generateCombinations(); 

     $combinationSize = $this->arraysSize; 
     $intersectionSize = 0; 

     foreach ($this->combinations as $combination) { 
      $intersection = call_user_func_array('array_intersect', $combination); 

      if ($combinationSize > count($combination)) { 
       $combinationSize = count($combination); 
       $intersectionSize = 0; 
      } 

      if (count($intersection) > $intersectionSize) { 
       $this->intersections[$combinationSize] = $intersection; 
       $intersectionSize = count($intersection); 
      }  
     } 
    } 

    private $combinationsMasks; 
    private function generateCombinationsMasks() 
    { 
     $combinationsMasks = []; 
     $totalNumberOfCombinations = pow(2, $this->arraysSize); 

     for ($i = $totalNumberOfCombinations - 1; $i > 0; $i--) { 
      $combinationsMasks[] = str_pad(
       decbin($i), $this->arraysSize, '0', STR_PAD_LEFT 
      ); 
     } 

     usort($combinationsMasks, function ($a, $b) { 
      return strcmp(strtr($b, ['']), strtr($a, [''])); 
     }); 

     $this->combinationsMasks = array_slice(
      $combinationsMasks, 0, -$this->arraysSize 
     ); 
    } 

    private $combinations; 
    private function generateCombinations() 
    { 
     $this->combinations = array_map(function ($combinationMask) { 
      return $this->generateCombination($combinationMask); 
     }, $this->combinationsMasks);  
    } 

    private function generateCombination($combinationMask) 
    { 
     $combination = []; 
     foreach (str_split($combinationMask) as $key => $indicator) { 
      if ($indicator) { 
       $combination[] = $this->arrays[$key]; 
      } 
     } 

     return $combination; 
    } 
} 

私はメソッドに自明な名前を付けることを試みました。いくつかのコードを最適化することができます(たとえば、count関数を同じ配列に複数回呼び出す;これは変数を減らすために行われました)。

したがって、基本的にロジックはかなりシンプルです。配列のすべての組み合わせを生成し、使用された配列の数によって減少させます。次に、各長さの組み合わせの最長交差点を見つけます。実際、これは最も難しい部分です。 1つの特定の交差点を得るために、最初に閾値と一致するものを戻す。

$intersections = new Intersections([$a, $b, $c, $d]); 

var_dump($intersections->getAll()); 
var_dump($intersections->getByThreshold(3)); 

ここはworking demoです。

他の方法ですべての組み合わせを見つけることができます(例:one from "PHP Cookbook")。好きなものを選ぶことができます。

+0

これは私が探していたものです、ありがとう! – Jelle

+0

@Jelle注意しておきますが、サイズ1(配列はそれ自身と交差する)の組み合わせは除外されます。これは端の場合であり、array_intersectに1つの引数を渡すことはできません。だから、あなた自身で自由に追加することができます。 '$配列'から最大長の配列を '$ intersections'配列の末尾にキー' 1'で追加するメソッドを実装できます。 – sevavietl

0

そのための機能は組み込まれていません。

$values = []; 

foreach ([$a, $b, $c, $d] as $arr) 
    foreach ($arr as $value) 
     $values[$value] = ($values[$value] ?? 0) + 1; 

// For threshold of 3 
$values = array_keys(array_filter($values, function($a) { return $a >= 3; })); 

注:これはPHP7のために必要ですか?オペレーター。そうでなければ、次のようなものを使用してください:

$values[$value] = empty($values[$value]) ? 1 : $values[$value] + 1; 
+0

ありがとう!それは非常にエレガントなソリューションです! – Jelle

+0

編集:私は少し銃を飛び越した:これは私に配列の少なくとも3つに発生する要素の配列を与えるだろうか?それは、私が予選で決めたものではないので、私はそれを反映するために投稿を編集した。 – Jelle

+0

私はそれを達成するためにはもっと複雑だと思う、それは考慮すべき他の変数だからだ。異なる配列の組み合わせで繰り返される要素の数が同じ場合、どの要素が除外されるのでしょうか? – vuryss