【问题标题】:How to find a set of elements of a specific size that include the greatest possible sets in a collection of sets?如何找到一组特定大小的元素,其中包括集合中可能的最大集合?
【发布时间】:2020-08-17 22:06:40
【问题描述】:

给定一个基数在 1 到 6 之间的有限集族,它们本身是更大的可能元素有限集的子集,我的目标是创建最具包容性的元素集,即创建的元素集合可用于从集合家族中创建最可能的集合。我不完全确定我是否正确地陈述了这个问题,因为我已经很久没有在学校研究这些类型的问题了,但让我试着通过一个例子来澄清一下。

以下是最大元素数为 6 的集合族示例:

{1029}
{1029}
{1049}
{1029,1049}
{1029,1049,1118,1125}
{1029,1112,1125,1505}
{1029,1049,1094,1112,1505,1525}
{1029,1125,1138,1505,1525}
{1049,1094,1125,1182,1525,1531}

这些集合是由更大的有限元素集合创建的:

{1029,1049,1094,1112,1118,1125,1138,1182,1505,1525,1531}

我们的目标是创建不超过 6 个元素的集合,这些元素可用于在族中重新创建最大数量的集合。这是一个答案集的示例:

Created Set 1: {1029,1049,1112,1118,1125,1505}

创建的集合的元素可用于重新创建家庭中的 6 个集合。

{1029}
{1029}
{1049}
{1029,1049}
{1029,1049,1118,1125}
{1029,1112,1125,1505}

希望这是有道理的。显然,集合族和可能元素的集合一样大得多。我还需要重复这个创建集合的过程,直到可以重新创建家庭中的所有集合。

我真的在寻找一种算法解决方案,用任何编程语言或伪代码编写都会特别有用,但即使是我可以用来翻译成代码的数学公式也很有帮助。如果我能弄清楚这类问题的名称是什么,我什至会很高兴,这样我就可以自己进行更有效的研究。谢谢!

【问题讨论】:

  • 假设您已经创建了集合 A 和 B,您是否允许通过从 A 中选择一些元素和从 B 中选择一些元素来创建集合?例如,如果 1 在 A 中,但不在 B 中,2 在 B 但不在 A 中,如果您的原始集合之一是 {1,2},您会说可以创建吗?
  • 感谢您的回复@dmuir。是的,您可以从集合中的任何集合中选择任何元素。目标是最大化创建的集合覆盖率。经过进一步研究,我认为这可能是 Set Cover Problem 的一个变体,但我试图消化的大部分资源都是学术文章,老实说有点过头了。任何见解都值得赞赏!

标签: algorithm set mathematical-optimization


【解决方案1】:

在我看来,这很简单。作为术语,我将您给定的集合称为原始集合,而您制造的集合称为选定的集合。这与集合覆盖问题之间的主要区别在于,您不需要从原始集合中获取所选集合。

当且仅当原始集合是所选集合的子集时,才能从所选集合“创建”原始集合。类似地,如果原始集合是所选集合的并集的子集,则可以从所选集合的集合中创建原始集合。因此,如果原始集的并集是所选集的并集的子集,则可以创建所有原始集。因此,您可以通过取原始集合的并集然后将该并集(以您喜欢的任何方式)划分为不超过 6 个元素的集合来制造所选集合。

【讨论】:

  • 再次感谢@dmuir,我基本上遵循了相同的路径,但我不知道我是否以最佳方式进行操作。我将在今天下午晚些时候与您分享我的代码或伪代码,希望您有时间回顾一下,您可以提供任何见解或回复!
【解决方案2】:

好的,所以我想出了一个答案,尽管它可能不是有史以来最优化的解决方案。如果有人可以帮助我更快地运行它,或者可以帮助我以能够找到最佳前 3 名的方式编写它,我将不胜感激。这是我的代码,注意我使用的是数据结构“Set”,可从https://github.com/php-ds获得

class ProductionOptimizer{
    private int $CARDINALITY=6;
    private CombinationsIterator $combinationsIterator;
    private array $tickets;
    private Set $threads;

    public function __construct(array $array){
        $this->tickets=$array;
        $start = microtime(TRUE);
        $this->threads=new Set();
        foreach ($array as $ticketThreads){
            foreach ($ticketThreads as $thread)
                $this->threads->add($thread);
        }
        $end = microtime(TRUE);
        // print_r($this->threads);
        echo PHP_EOL."Creating the Set took " . ($end - $start) . " seconds to complete.";
        $this->combinationsIterator=new CombinationsIterator($this->getThreadsAsArray(),6);
    }

    public function outputThreads() : void {
        print_r($this->threads);
    }

    public function getThreadsAsArray() : array {
        return $this->threads->toArray();
    }

    public function getCombinationsIterator() : CombinationsIterator{
        return $this->combinationsIterator;
    }

    public function createNewCombinationsIterator(array $array, int $subsetCardinality=null) : CombinationsIterator {
        if(is_null($subsetCardinality)) $subsetCardinality=$this->CARDINALITY;
        $this->combinationsIterator=new CombinationsIterator($array, $subsetCardinality);
        return $this->combinationsIterator;
    }

    public function removeFromSet(array $array){
        // var_dump($this->threads);
        echo "Removing Threads(before count :".$this->threads->count().")...".PHP_EOL;
        $this->threads->remove(...$array);
        // var_dump($this->threads);
        echo "Removing Threads(after count: ".$this->threads->count().")...".PHP_EOL;
        $this->combinationsIterator=new CombinationsIterator($this->getThreadsAsArray(),6);
    }

    public function getSet() : Set {
        return $this->threads;
    }
    public function getThreads() : Set {
        return $this->threads;
    }

    /* not need if using Set class */
    /*
        Set:
            The code took 4.5061111450195E-5 seconds to complete.
        This Function:
            The code took 0.0054061412811279 seconds to complete.
    */
    public function deduplicateArray($array){
        array_walk_recursive($array, function($v) use (&$r){$r[]=$v;});
        return array_values(array_unique($r));
    }

}

class CombinationsIterator implements Iterator
{
    protected $c = null;
    protected $s = null;
    protected $n = 0;
    protected $k = 0;
    protected $pos = 0;

    function __construct($s, $k) {
        if(is_array($s)) {
            $this->s = array_values($s);
            $this->n = count($this->s);
        } else {
            $this->s = (string) $s;
            $this->n = strlen($this->s);
        }
        $this->k = $k;
        $this->rewind();
    }
    function key() {
        return $this->pos;
    }
    function current() {
        $r = array();
        for($i = 0; $i < $this->k; $i++)
            $r[] = $this->s[$this->c[$i]];
        return is_array($this->s) ? $r : implode('', $r);
    }
    function next() {
        if($this->_next())
            $this->pos++;
        else
            $this->pos = -1;
    }
    function rewind() {
        $this->c = range(0, $this->k);
        $this->pos = 0;
    }
    function valid() {
        return $this->pos >= 0;
    }

    protected function _next() {
        $i = $this->k - 1;
        while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
            $i--;
        if($i < 0)
            return false;
        $this->c[$i]++;
        while($i++ < $this->k - 1)
            $this->c[$i] = $this->c[$i - 1] + 1;
        return true;
    }

}

这里有一些要测试的样本数据。这将获得前 3 个有序序列,并在每次迭代时从集合的宇宙中删除匹配的集合:

function humanReadableMemory($size)
{
    $unit=array('b','kb','mb','gb','tb','pb');
    return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i];
}
$tickets = [
    [1029],
    [1029],
    [1029],
    [1029],
    [1029],
    [1029],
    [1029],
    [1029, 1049],
    [1029, 1049],
    [1029, 1049, 1080, 1112, 1125, 1188],
    [1029, 1049, 1125],
    [1029, 1049, 1188, 1278],
    [1029, 1056, 1138, 1158, 1182, 1514, 1531],
    [1029, 1071, 1076],
    [1029, 1074, 1075, 1092, 1093, 1242, 1523, 1525],
    [1029, 1094, 1125, 1138, 1158],
    [1029, 1094, 1125, 1278],
    [1029, 1094, 1182, 1221, 1242, 1505],
    [1029, 1094, 1278, 1298],
    [1029, 1094, 1298],
    [1029, 1112, 1125, 1298, 1516],
    [1029, 1112, 1125, 1505],
    [1029, 1112, 1505],
    [1029, 1125],
    [1029, 1125],
    [1029, 1125, 1138],
    [1029, 1138, 1188, 1514, 1517],
    [1029, 1138, 1242, 1317, 1505, 1525],
    [1029, 1242],
    [1029, 1242],
    [1029, 1242, 1270, 1524],
    [1029, 1242, 1370, 1505],
    [1029, 1298],
    [1029, 1298, 1505],
    [1029, 1317],
    [1029, 1505],
    [1029, 1505],
    [1029, 1505, 1525],
    [1038, 1076, 1177, 1182],
    [1045, 1048, 1097, 1100],
    [1046, 1125, 1182, 1242, 1278],
    [1049],
    [1049],
    [1049],
    [1049],
    [1049],
    [1049],
    [1049],
    [1049],
    [1049]
];
$po=new ProductionOptimizer($tickets);
$start = microtime(TRUE);
$end = microtime(TRUE);
echo "Current Memory Usage: ".humanReadableMemory(memory_get_usage()).PHP_EOL;
echo "Peak Memory Usage: ".humanReadableMemory(memory_get_peak_usage()).PHP_EOL;
echo "Starting Iterations and Combination Testing...".PHP_EOL;
$rankedOutput=[];
$start = microtime(TRUE);
for ($i=0;$i<3;$i++) {
    $matchCountArray=[];$bestChoice=0;$bestChoiceId=-1;
    foreach ($po->getCombinationsIterator() as $comboTest){
        $combinationString=implode(",",$comboTest);
        $matchCountArray[$combinationString]=0;
        $comboTestSet=new Set($comboTest);
        foreach ($tickets as $ticketIdx=>$ticketElements) {
            $ticketElementsSet = new Set($ticketElements);
            $testsArray[$combinationString]+=($comboTestSet->contains(...$ticketElements) ? 1 : 0);
        }
        if ($matchCountArray[$combinationString]>$bestChoice) {
            $bestChoice = $matchCountArray[$combinationString];
            $bestChoiceId=$combinationString;
        } else {
            //trying to save memory
            unset($matchCountArray[$combinationString]);
        }
    }
    $currentBestChoiceSet=new Set(explode(",",$bestChoiceId));
    $currentUniverseSet=$po->getSet()->copy();
    for($tmpJ=0;$tmpJ<$currentUniverseSet->count();$tmpJ++){
        $testAgainstSet=$currentUniverseSet->get($tmpJ);
        if ($currentBestChoiceSet->contains(...$testAgainstSet->toArray())){
            $currentUniverseSet->remove($testAgainstSet);
        }
    }
    $tickets = [];
    foreach ($currentUniverseSet as $singleSet){
        $tickets[]=$singleSet->toArray();
    }
    $po=new ProductionOptimizer($tickets);
    $rankedOutput[$i]=["greatest_matching_sequence"=>$bestChoiceId, "coverage_count"=>$bestChoice];
}
echo "RankedOutput:".PHP_EOL;
var_dump($rankedOutput);
echo PHP_EOL;
$end = microtime(TRUE);
echo PHP_EOL."The code took " . ($end - $start) . " seconds to complete.".PHP_EOL;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多