MiniZinc 数组中字符串值的索引

Zer*_*ro3 3 constraints minizinc

问题

给定一个 MiniZinc 字符串数组:

int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;
Run Code Online (Sandbox Code Playgroud)

...从 MiniZinc 数据文件加载数据:

numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];
Run Code Online (Sandbox Code Playgroud)

如何在数组中查找特定字符串的索引?例如,ARMOR 位于位置 2。

上下文

我需要根据统计数据的一些限制找到最佳的项目选择。此信息存储在一个二维数组中,声明如下:

int: numItems;
set of int: Items = 1..numItems;
array[Items, Stats] of float: itemStats;
Run Code Online (Sandbox Code Playgroud)

因此,为了对通过所选项目获得的最小 ARMOR 数量编写约束,我需要知道 ARMOR 在内部数组中的索引为 2。

由于数据文件是由外部程序生成的,并且统计信息的数量和顺序是动态的,因此我无法对约束中的索引进行硬编码。

一种解决方案(在我的情况下不起作用)

MiniZinc教程使用了一个有趣的技巧来实现类似的东西:

set of int: Colors = 1..3;
int: red = 1;
int: yellow = 2;
int: blue = 3;
array[Colors] of string: name = ["red", "yellow", "blue"];

var Colors: x;
constraint x != red;
output [ name[fix(x)] ];
Run Code Online (Sandbox Code Playgroud)

不幸的是,由于 MiniZinc 数据文件中不允许变量声明,这个技巧在我的情况下不起作用。

And*_*rey 5

您可以编写自己的自定义函数来获取字符串数组中字符串的索引:

function int: getIndexOfString(string: str, 
                               array[int] of string: string_array) = 
   sum(  [ if str = string_array[i] 
              then i
           else 0 endif  
          | i in index_set(string_array) ]
   );
Run Code Online (Sandbox Code Playgroud)

在这个函数中创建一个整数数组,其中在位置的整数i或者等于索引str如果string_array[i]=str0其它。例如,对于您的示例字符串数组["HEALTH", "ARMOR", "MANA"]和 str ARMOR,生成的 int 数组将为[0,2,0].

这就是为什么我可以简单地对 int 数组求和以获得字符串的索引。如果字符串没有出现,则返回值是0,这很好,因为 MiniZinc 中的索引默认从 1 开始。

以下是您如何为第一个示例调用上述函数:

int: numStats;
set of int: Stats = 1..numStats;
array[Stats] of string: statNames;

numStats = 3;
statNames = ["HEALTH", "ARMOR", "MANA"];

var int: indexOfArmor;

constraint 
   indexOfArmor = getIndexOfString("ARMOR",statNames);  

solve satisfy;  
Run Code Online (Sandbox Code Playgroud)

但是请注意,上述功能是有限的并且存在一些缺陷。首先,如果您在数组中多次出现该字符串,那么您将收到一个无效索引(出现的所有索引的总和str)。此外,如果您为字符串数组设置了自己的索引(比如(2..6)),那么您将需要调整该函数。