矩阵组合逻辑

时间:2012-11-23 15:31:17

标签: php matrix logic

注意:**请阅读所有其他相关问题:**

这是我第一次和第二次尝试提出这个问题:

问题在于:

  • 我有几个(如20个)布尔验证(真/假)
  • 所有布尔验证作为一个整体也具有验证结果

我正在尝试找到测试所有验证和验证结果的最佳解决方案。我正在研究一个矩阵,以保留所有可能的组合,但这可能是一种过度杀伤。

这是一个例子(1 - 20):

  • test_1 =有30人丧生
  • test_2 =已找到地图1
  • test_3 =已获得Mastered Level 1
  • test_4 =已达到咕噜状态
  • test_5 =具有突击武器
  • test_6 =有刀
  • test_7 =拥有Grenade
  • test_x = Etc ......

因此,当播放器将所有这些验证都设为TRUE时,我可以给出一个级别结果

  • 如果test_1,test_2,test_3(三者的任意组合):level = green

所有组合均为(15):

  • TEST_1
  • test_2
  • test_3
  • test_1,test_2
  • test_1,test_3
  • test_2,test_1(重复可跳过此内容)
  • test_2,test_3
  • test_3,test_1(重复可以跳过此内容)
  • test_3,test_2(重复可以跳过此内容)
  • test_1,test_2,test_3
  • test_1,test_3,test_2(重复可跳过此内容)
  • test_2,test_1,test_3(重复可跳过此内容)
  • test_2,test_3,test_1(重复可以跳过此内容)
  • test_3,test_1,test_2(重复可跳过此内容)
  • test_3,test_2,test_1(重复可以跳过此内容)

所以独特的组合是(7而不是15):

  • TEST_1
  • test_2
  • test_3
  • test_1,test_2
  • test_1,test_3
  • test_2,test_3
  • test_1,test_2,test_3

现在,我正在尝试找到最佳解决方案,为所有20个验证找到独特的组合,并从该矩阵中进行级别验证。

更新:

此外,我只需要找到TRUE组合,这样你就可以阅读这样的独特组合:

  • TEST_1
  • test_2
  • test_3
  • test_1,test_2
  • test_1,test_3
  • test_2,test_3
  • test_1,test_2,test_3

验证测试的布尔值结果

  • TRUE,FALSE,FALSE
  • FALSE,TRUE,FALSE
  • FALSE,FALSE,TRUE
  • TRUE,TRUE,FALSE
  • TRUE,FALSE,TRUE
  • FALSE,TRUE,TRUE
  • TRUE,TRUE,TRUE

所以这些组合中的任何一个都是绿色等级。

另外,我需要知道测试验证的顺序以及比较级别分配的矩阵顺序。所以对于GREEN级别我只需要测试1,2和3的验证结果组合矩阵。所以我可以忽略测试4 - 20

更新#2:

我知道这看起来像一个简单的OR条件,但我想取出组合逻辑将级别设置为矩阵。我可以使用组合矩阵来确定级别逻辑,而无需在代码本身中编写额外的代码或修改当前逻辑。我想比较一组给定测试的验证结果,并为这些结果分配一个级别。验证组合的不同排列将导致不同的级别分配。

我知道我可以在代码本身中添加组合逻辑,但由于这种逻辑看起来非常不稳定,并且认为这可能提供更灵活的解决方案。 建议?

4 个答案:

答案 0 :(得分:1)

(为了清楚起见,删除了我之前的两个答案)

在您上次修改后,我首先要确保100%理解您想要的“级别检测算法”。

如果我理解得很好,你想定义/维护一个简单的配置结构,告诉哪些测试给出了哪个级别。

e.g。使用关联数组:

array(
  'green' => array('test1', 'test2', 'test3'),
  'orange' => array('test2', 'test3', 'test5')
  ...
  );

具有以下含义:如果满足列表中的一个或多个测试,则将该级别(数组键)分配给播放器。这样的逻辑可以很容易地涵盖很多组合,并且可以避免处理巨大的矩阵。

也许你想扩展逻辑来告诉我,例如,测试列表中至少有N个测试得到满足。

array(
  'green' => array(
      'tests' => array('test1', 'test2', 'test3'),
      'nb_required' => 2
    ),
  ...
  );

这就是你想要的吗?

顺便说一句,你为什么不使用经典的XP /升级系统? :-P

答案 1 :(得分:1)

<强>简介

您可以轻松获得这样的组合:

echo "<pre>";
$test = ["test_1","test_2","test_3"];

// Get Combination
$return = uniqueCombination($test);

//Sort
sort($return);

//Pretty Print
print_r(array_map(function($v){ return implode(",", $v); }, $return));

function uniqueCombination($in, $minLength = 1, $max = 10) {
    $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 ++)
            $b{$j} == '1' and $out[] = $in[$j];

        count($out) >= $minLength && count($out) <= $max and $return[] = $out;
    }
    return $return;
}

输出

Array
(
    [0] => test_1
    [1] => test_2
    [2] => test_3
    [3] => test_1,test_2
    [4] => test_1,test_3
    [5] => test_2,test_3
    [6] => test_1,test_2,test_3
)

问题

它们大约是1,048,576组合,我相信这不是你想要的那种阵列我建议基于条件的组合而不是所有可能的组合

示例

// Game Conditions
$game = new Game();
$game->addCondition(new Condition(new Level(1), new Kill(30)));
$game->addCondition(new Condition(new Level(2), new Map(1), new Kill(10)));
$game->addCondition(new Condition(new Level(3), new Grunt(10)));
$game->addCondition(new Condition(new Level(4), new Knife(1), new Ak47(1)));
$game->addCondition(new Condition(new Level(5), new Grenade(1), new Combo(7)));
$game->addCondition(new Condition(new Level(6), new Kill(100), new Blow(10), new Stab(10)));
$game->addCondition(new Condition(new Level(7), new Herb(10), new Medipack(1), new Map(1), new Artwork(1)));
$game->addCondition(new Condition(new Level(8), new Grenade(20),new Artwork(5)));


// User Starts Game
$user = new User($game);


$user->task(new Map(1));
$user->task(new Herb(5));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Herb(10));
$user->task(new Kill(10));
$user->task(new Kill(10));
$user->task(new Ak47(1));
$user->task(new Knife(1));
$user->task(new Map(1));
$user->task(new Grunt(17));
$user->task(new Kill(60));
$user->task(new Combo(1));
$user->task(new Kill(40));
$user->task(new Medipack(1));
$user->task(new Artwork(1));
$user->task(new Grenade(1));
$user->task(new Combo(10));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Blow(10));
$user->task(new Stab(5));
$user->task(new Stab(5));

printf("\n<b>Total Point %s",number_format($user->getPoint(),0));

输出

+Task Map   Added  (1)
+Task Herb  Added  (5)
+Task Kill  Added  (10)
^Task Kill  Updated (20)
^Task Herb  Updated (15)
^Task Kill  Updated (30)

*Level 1 Completed* 


*Level 2 Completed* 

^Task Kill  Updated (40)
+Task Ak47  Added  (1)
+Task Knife     Added  (1)
^Task Map   Updated (2)
+Task Grunt     Added  (17)

*Level 3 Completed* 


*Level 4 Completed* 

^Task Kill  Updated (100)
+Task Combo     Added  (1)
^Task Kill  Updated (140)
+Task Medipack  Added  (1)
+Task Artwork   Added  (1)
+Task Grenade   Added  (1)
^Task Combo     Updated (11)

*Level 5 Completed* 

+Task Blow  Added  (10)
+Task Stab  Added  (5)
^Task Blow  Updated (20)
^Task Stab  Updated (10)

*Level 6 Completed* 


*Level 7 Completed* 

^Task Stab  Updated (15)


<b>Total Point 1,280</b>

使用的课程

class Task {
    private $no;

    function __construct($no = 1) {
        $this->no = $no;
    }

    function getNo() {
        return $this->no;
    }

    function getName() {
        return get_called_class();
    }

    function merge(Task $task) {
        $this->no += $task->getNo();
        return $this;
    }
}
class User {
    private $game;
    private $point;
    private $tasks = array();

    function __construct(Game $game) {
        $this->game = $game;
    }

    function getPoint() {
        return $this->point;
    }

    function getTask() {
        return $this->tasks;
    }

    function task(Task $task) {
        if (isset($this->tasks[$task->getName()])) {
            $this->tasks[$task->getName()]->merge($task);
            printf("^Task %s \tUpdated (%s)\n", $this->tasks[$task->getName()]->getName(), $this->tasks[$task->getName()]->getNo());
        } else {
            printf("+Task %s \tAdded  (%s)\n", $task->getName(), $task->getNo());
            $this->tasks[$task->getName()] = $task;
        }

        $this->point += $task->getNo() * $task->d;
        $this->game->notify($this);
    }
}
class Condition {
    private $task = array();
    private $status = false;

    function __construct(Level $level) {
        $this->level = $level;
        $tasks = func_get_args();
        array_shift($tasks);
        $this->task = new SplObjectStorage($tasks);
        foreach ( $tasks as $task )
            $this->task->attach($task);
    }

    function update(Game $game, User $user) {
        if ($this->status)
            return;
        $n = 0;
        foreach ( $this->task as $cTask ) {
            foreach ( $user->getTask() as $task ) {
                if ($cTask->getName() == $task->getName()) {
                    if ($task->getNo() >= $cTask->getNo())
                        $n ++;
                }
            }
        }
        if ($n === count($this->task) && ($game->getLevel()->getNo() + 1) == $this->level->getNo()) {
            $this->status = true;
            $game->setLevel($this->level);
            printf("\n*Level %d Completed* \n\n", $this->level->getNo());
        }
    }

    function getStatus() {
        return $this->status;
    }
}
class Game {
    private $taskCondition;
    private $level;

    public function __construct() {
        $this->taskCondition = new SplObjectStorage();
        $this->level = new Level(0);
    }

    function setLevel(Level $level) {
        $this->level = $level;
    }

    function getLevel() {
        return $this->level;
    }

    function addCondition($condition) {
        $this->taskCondition->attach($condition);
    }

    public function notify($user) {
        foreach ( $this->taskCondition as $conditions ) {
            if ($conditions->getStatus() === true) {
                // detached completed condition
                $this->taskCondition->detach($conditions);
                continue;
            }
            $conditions->update($this, $user);
        }
    }

    public function hasCondition() {
        return count($this->taskCondition);
    }
}

class Level extends Task{}
class Action extends Task{};
class Weporn extends Task{};
class Skill extends Task{};
class Tresure extends Task{};
class Medicine extends Task{};

class Kill extends Action{public $d = 5 ;};
class Blow extends Action{public $d = 7 ;};
class Stab extends Action{public $d = 10 ;};

class Map extends Tresure{public $d = 10 ;};
class Artwork extends Tresure{public $d = 20 ;};

class Knife extends Weporn{public $d = 5 ;};
class Grenade extends Weporn{public $d = 10 ;};
class Ak47 extends Weporn{public $d = 10 ;};

class Jump extends Skill{public $d = 2 ;};
class Grunt  extends Skill{public $d = 4 ;};
class Combo extends Skill{public $d = 7 ;};

class Medipack extends Medicine{public $d = 5 ;};
class Herb extends Medicine{public $d = 5 ;};

Simple Online Demo

答案 2 :(得分:1)

不完全回答你的问题,但似乎你错过了什么。

假设您有二十个测试,标记为1但 n 。决定是否要验证测试 n 。要么包含测试验证,要么不包括。这是每次测试的两个选择。继续( n -1)直到您没有更多测试。对于 n = 20,那是2 ^ 20 = 1048576个可能的测试组合(包括一个你没有选择任何测试的组合),这意味着1048576个结果。现在我仍然不明白你的“验证水平”是什么意思,但我不得不想知道为什么你首先需要那么多的测试组合。

编辑:嗯,一个测试矩阵(可以这么说)可以工作....但你仍然需要生成矩阵。您可能不会手动编码所有1048576组合。但是假设你已经创建了映射,你只需要一个零索引数组作为一个具有1048576值的查找表,你就可以完成了!你需要将测试结果映射到矩阵的所有内容都是为每个测试分配一个二进制数字(test1将是一个位置,test2将是2的位置,等等)

我怀疑你真正想要的是一个快速的方法来生成映射给出一些你可能在php中编码的更广泛的规则......但是这里有趣的部分。如果你有一些代码为任何和每组测试生成矩阵,那么矩阵就是多余的;您的代码本质上是矩阵的压缩表示。使用一个而不是另一个的唯一一点是一个人是否会比另一个人更快。

似乎你真的不关心所有1048576组合。我怀疑将测试划分为他们自己的一组测试(一个用于红色,一个用于蓝色等)可能会对您有所帮助。例如,如果您将测试分成5组,并且每组有3种不同的可能性(而不是16种),那么您只能使用(每组3个不同的结果)^(4组)= 81独特的结果。 81个独特的结果比100多万个更易于管理。

对于不同的独立事物,您似乎也可能需要不同的分区。只要一定数量的真实,即使哪个测试结果为真,也可能无关紧要,即“目标:满足5个蓝色目标中的3个”目标:10个红色中的7个和蓝色的目标相遇“或其他什么。那些测试必须是独立访问的,并不一定能与其他测试的结果相乘 - 如果没有达到你的蓝色目标,那么永远不会有10个红色和蓝色目标得到满足(我会承认,如果没有例子,这很难解释。

所以,真的,没有快速回答。要么处理1048576组合中的所有组合单独,要么为您的测试创建和编码某种通用分组方案,以大幅减少组合。您当然可以通过所述方案创建完整的矩阵方案,甚至可以为给定的一组组合动态生成完整的1048576元素矩阵。但你只能燃烧一半蜡烛就不能烧掉整个蜡烛。

编辑II(和III):

我要再猜一次。您提出的级别似乎与每个(以及之前的批次)完成的目标数量相关。因此,将结果编码为一串(“T”和“F”),并计算每批的“F”,并从那里确定。如果Fs的数量是字符串中的字符数,则批次的目标没有完成,如果Fs的数量为零,则批次的所有目标都完成。

让我们说蓝色后的批次是紫色的。如果没有完成所有绿色批次,有人可以实现紫色目标吗?如果是这样,你可以为这种可能性分配另一种颜色;如果不是,那么,我会假设这些级别是有序的(绿色和蓝色之间的橙色,可能是“棕色”或蓝色和紫色之间的东西 - 你可以说我凭空捏出它 - 它应该相当简单通过这些计数级联来确定当前的“水平”。

如果没有排序,那么它与我上面提到的情况非常类似:每个小组都有一个结果{“没有完成目标”,“完成了一些目标”,“完成了所有目标”}。有x个组意味着3^x个可能的结果。您应该将基础矩阵基于这些结果,而不是前面提到的测试结果。

答案 3 :(得分:0)

我已经找到了你提到的问题的图形显示。

|0|1|2|3|4|5|6|7|8|9|
|1|T|T|T|T|T|T|T|T|T|
|2|F|T|T|T|T|T|T|T|T|
|3|F|F|T|T|T|T|T|T|T|
|4|F|F|F|T|T|T|T|T|T|
|5|F|F|F|F|T|T|T|T|T|
|6|F|F|F|F|F|T|T|T|T|
|7|F|F|F|F|F|F|T|T|T|=>[7,9] if this is the answer
|8|F|F|F|F|F|F|F|T|T|
|9|F|F|F|F|F|F|F|F|T|

在我看来,你应该以反向对角顺序检查条件,然后从左到右。 这意味着

首先检查[9,9]

如果失败,请检查[8,8]

如果失败则检查[7,7]

如果它给出了真正的检查[7,8]

如果这给出了错误的检查[7,9],这必须是答案和简单的快捷方式,

此方法将减少所有处理时间。