【问题标题】:Complex Combinatorial Algorithms复杂组合算法
【发布时间】:2013-07-25 00:49:39
【问题描述】:

因此,Wendy's 宣传他们的三明治有 256 种组合——这意味着有 8 种成分你可以没有(虽然我想知道他们为什么会认为你什么都不包括的组合是有效的,但我离题了)。

通用方法允许您将每个选择的各种状态相乘,从而实现更复杂的组合。在这种情况下,只能包含或排除 Wendy 的项目。但有些三明治可能有两种芥末可供选择(但不能同时使用两种芥末,以节省成本)。

这些都相当简单。你将选项的数量相乘,所以对于 Wendy's 来说:

2*2*2*2*2*2*2*2 = 256

如果他们像上面那样多样化他们的芥末选择,那就是:

2*2*3*2*2*2*2*2 = 384

走得更远似乎更难。

如果您将芝麻作为单独的项目,那么他们需要面包项目。包子可以有芝麻,包子可以不包子,芝麻不能包子。这可以简化为具有三种状态(无、有种子的小圆面包、无种子的小圆面包)的单个小圆面包项目,但在某些情况下无法做到这一点。

例如,戴尔的计算机配置器不允许某些组合(可能插槽已满,放入同一系统时项目不兼容等)。

  • 在处理项目可能发生冲突的复杂得多的系统时,有哪些合适的组合方法?
  • 有什么好的、通用的方法来存储此类信息,而无需为每个产品/组合/项目编写代码来捕获冲突?
  • 当系统必须处理复杂的冲突组合时,是否有一种简单的方式可以说“有 X 种方式来配置您的系统/三明治”?

【问题讨论】:

  • 底部有 3 个真正的问题,用项目符号表示。对于人们为什么投票结束“不是一个真正的问题”,我将不胜感激 - 我应该以某种方式重新格式化它吗?
  • 是的,它只对正在寻找这个问题的答案的一小部分人有所帮助。就像一个关于如何转义字符串的问题只会帮助一小群正在寻找如何转义字符串的人。
  • @blesh - 我很欣赏它本质上非常普遍 - 这是故意的。在我开始构建这个系统之前,我想从多个角度来看待它,看看可以尝试哪些方法。然而,它对今天的程序员来说非常有效。有很多创作网站(hulu、createspace、cafepress)具有非常、非常、非常基本的组合和创建项目的能力。他们没有尝试解决这个难题,例如,如果解决了这个难题,他们的客户可以节省大量时间来制作具有相同图像的 20 种不同商品。定制是非常有利可图的。
  • 更不用说第一个具有此功能的开源购物车将比其他购物车获得巨大优势。不过,制作一个店主可以以简单方式创建规则的系统是另一个难题。
  • @AdamDavis 我已经为您添加了答案。我相信它不仅为使用动态和条件规则验证对象的方法提供了指导,而且还提供了一些属性和对象结构的具体示例供您使用。它不能完全回答问题 #3,但这是一个肯定可以解决的数学函数。让我知道你的想法,我会提供更多细节。我真的很想搞定这个。

标签: algorithm configuration combinatorics


【解决方案1】:

惠普位于加利福尼亚的高端服务器制造工厂多年来一直使用基于规则的定制系统来做到这一点。

工厂车间构建周期流程包括预先检查,以确保在将订单发布给构建者和测试者之前可构建。

其中一项检查确定订单的物料清单 (BOM) 是否符合流程工程师指定的一系列规则。例如,如果客户订购处理器,请确保他们也订购了足够的直流转换器部件;或者,如果他们订购了一定数量的内存 DIMM,请确保他们还订购了一个子板以容纳额外的容量。

具有编译器背景的计算机科学专业的学生会识别代码。代码解析 BOM,在内部生成按类型分组的零件线程树。然后,它将规则应用到内部树中,以确定顺序是否符合。

作为副作用,系统还为工人在构建每个系统时提取的每个订单生成构建文档。它还为构建后的老化过程生成了预期的测试结果,因此测试平台可以参考它们并确定一切是否正确构建。

【讨论】:

  • 有趣 - 将给定的组合视为自定义编译器的编译器输入。我得考虑一下。
  • 每个配置都有一个关联的规则。例如,“真”; “”(如果仅在特定零件/模型在 BOM 中时声明); “ GT 3”(当顺序超过三个部分时断言。存在许多其他规则。代码有一个自定义解析器来解析规则并执行逻辑。标准编译器理论,考虑到这并不奇怪程序员来自 UC-Chico 的学术教学背景。
  • @AdamDavis - 不要使用自定义解析器,而是在每个部件(低级基类和可覆盖)上放置一个验证函数,并通过依赖注入将当前产品的部件集合传递给它。这允许每个部分都使用自己的规则进行封装,并且能够将自己与添加到的产品进行比较。请看下面我的回答。非常感谢 Schultkl 提供了一个真实世界的故事。
【解决方案2】:

Adam Davis:如果我理解正确,您打算开发某种系统,实际上可以用于购物车,帮助用户购买兼容的零件。

问题定义

这是一个图形问题(它们不都是),你有与其他项目兼容的项目。例如Pentium i3-2020兼容任何Socket 1155 Motherboard,Asrock H61M-VS是Socket 1155 Motherboard,兼容2xDDR3(速度=1066),需要PCI-Express GPU,@ 987654326@、4 pin ATX 12V power等

您需要能够 (a) 确定篮子中的每件商品是否满足篮子中的另一件商品(即 RAM 卡具有兼容的主板),(b) 分配最合适的商品(即分配 USB 集线器如果主板的 USB 端口用完,则连接到主板 USB 端口和打印机到 USB 集线器,而不是反过来让集线器保持干燥),以及 (c) 为用户提供查找满意组件列表的功能。也许 USB 集线器总是可以优先考虑,因为它们是扩展(但请注意)。

您需要的数据结构

您将需要一个简单的分类系统,即 H61M-VS is-a 主板,H61M-VS has-a DDR3 内存插槽(速度属性为每个插槽)。

其次是分类和组合,你需要确定需求,这很简单。现在简单分类可以让一个简单的 SQL 查询找到所有符合分类的项目。

测试一个令人满意的篮子

要测试篮子,需要创建配置,确定哪些项目与哪些项目匹配(即主板的 DDR3 插槽与 4GB 内存模块匹配,SATA HDD 电缆连接到主板 SATA 端口和 PSU 的 SATA 电源线,而 PSU 的4针ATX 12V电源线连接主板。

最简单的就是检查是否存在另一个令人满意的项目

戴尔电脑配置器

你从一个项目开始,比如说一个处理器。处理器需要主板和风扇,因此您可以给他们选择主板(将处理器风扇添加到list_of_things_to_be_satisfied)。这种情况一直持续到list_of_things_to_be_satisfied 中没有更多项目。当然,这一切都取决于您的确切要求并了解您将为用户解决的什么问题。

【讨论】:

    【解决方案3】:

    有很多方法可以在代码中实现,但在我看来,这是在编程之前解决问题的最佳方法:

    定义零件和产品(前代码)

    在定义所有“部件”时,确定部件的层次结构和分类至关重要。这是正确的,因为某些规则可能是唯一部分独有的(例如“仅棕色芥末”),一些分类(例如“所有芥末”),有些是由输入(例如“所有调味品”)等

    构建规则集(前代码)

    为每个独特的零件、类别、类型和成品定义规则集(先决条件、排除等)。

    这听起来可能很愚蠢,但必须非常小心以确保在适当范围内定义规则。例如,如果成品是Burger:

    • 独特的物品规则 - “蘑菇仅适用于选定的蓝纹奶酪” prerequisite
    • 分类规则 - “只能选择 1 种芥末” exclusive
    • 类型规则 - “Pickles 与 Peppers 不兼容” exclusive

    在“零件”的独特/类别/类型规则上花费了这么多时间后,许多设计师会忽略仅适用于成品的规则,即使零件没有冲突。

    • 产品规则 - “最多 5 种调味品” condition
    • 产品规则 - “汉堡必须有面包” prerequisite

    这个规则图可以很快变得非常复杂。

    构建数据结构(代码)的建议

    1. 确保您的结构适应层次结构和分类。例如:“brown mustard”和“dijon mustard”是单独的对象,它们都是芥末,都是调味品。

      仔细选择继承建模(基类)和对象属性(例如Category 属性或HasCondiments 标志)的正确组合来完成这项工作。

    2. 在每个分层对象级别为RuleSets 创建一个私有字段。

    3. 为HasConflicts 标志和RuleViolations 集合创建公共属性。

    4. 当一个部件被添加到一个产品中时,检查所有级别的规则(它自己的、类别、类型和产品)——通过一个可以从产品。或者为了更好地内化,您可以在部件本身上制作一个事件处理程序。

    编写你的算法(代码)

    这是我很烂的地方,好在它超出了你的问题范围。

    这一步的诀窍是如何在代码中实现沿树/图向上传播的规则——例如,当特定部分与超出其范围的另一部分存在问题时,或者它的验证在何时运行另一部分被添加?我的想法:

    1. 对每个部分使用公共函数方法。将产品的CurrentParts 集合传递给它。

    2. 在 Product 对象上,定义处理程序以处理 OnPartAdded 和 OnPartRemoved,并让它们枚举 CurrentParts 集合并调用每个部分的验证函数。

    简单的原型示例

    interface IProduct
    {
        void AddPart();
        void OnAddPart();
    }
    // base class for products
    public class Product() : IProduct
    {
         // private or no setter. write functions as you like to add/remove parts.
        public ICollection<Part> CurrentParts { get; };
        // Add part function adds to collection and triggers a handler.
        public void AddPart(Part p)
        {
            CurrentParts.Add(p);
            OnAddParts();
        }
        // handler for adding a part should trigger part validations
        public void OnAddPart()
        {
            // validate part-scope rules, you'll want to return some message/exception
            foreach(var part in CurrentParts) {
                part.ValidateRules(CurrentParts); 
            }
            ValidateRules(); // validate Product-scope rules.
        }
    }
    
    interface IProduct
    {
        // "object" should be replaced with whatever way you implement your rules
        void object RuleSet; 
        void ValidateRules(ICollection<Part> otherParts);
    }
    // base class for parts
    public class Part : IPart
    {
        public object RuleSet; // see note in interface.
    
        public ValidateRules(ICollection<Part> otherParts)
        {
            // insert your algorithms here for validating 
            // the product parts against this part's rule set.
        }
    }
    

    干净整洁。

    【讨论】:

      【解决方案4】:

      作为一名程序员,我会做以下事情(尽管我在现实生活中实际上从未这样做过):

      • 算出总数 组合,通常是顺子 选项的乘法为 在您的问题中说明就足够了。 没有必要存储所有这些 组合。
      • 然后将您的总数除以 例外。例外情况可以是 存储为一组规则, 有效地说出哪些组合 不允许。
      • 计算总数量 允许的组合,您将拥有 贯穿整个集合 例外规则。

      如果您将所有组合视为一个集合,那么例外只会删除该集合的成员。但是您不需要存储整个集合,只需要存储例外,因为您可以很容易地计算集合的 大小。

      【讨论】:

        【解决方案5】:

        “Generating Functions”作为解决此类问题时可以使用的一种结构出现在脑海中。我会注意到,根据您的需要,有几种不同的生成函数。

        在北美,汽车牌照在计算所有排列时可能是一个有趣的组合问题.然而,一些组合由于其中一些含有脏话或种族主义词而被取消资格,这导致了一个稍微困难的问题。例如,有一个臭名昭著的 N 词,它至少有几个不同的拼写,我认为这些拼写是不允许出现在车牌上的。

        另一个例子是使用包含重复多次的某些项目的给定字母表来确定所有不同的单词顺序。例如,如果一个人想要所有不同的方式来排列单词“字母”的字母,那么它不仅仅是 6 个! “abcdef”就是这种情况,因为有 2 对字母使得计算起来有点棘手。

        L33t 可能是另一种在识别不当词时带来更多复杂性的方式.我不确定车牌上是否会出现许多特殊字符(如 $ 或 @),但可以将网络内容的家长控制视为必须使用这些算法来识别要审查的术语。

        【讨论】:

        • “字母”问题不难计算。它基本上是 n!/(l!e!t!r!)。在更一般的情况下,将字母数量的阶乘除以每个字母的数量的阶乘。
        【解决方案6】:

        您可能希望创建一个数据结构来唯一地表示单个配置。然后,每个兼容性规则都应该以一种方式定义,它可以生成一个集合,其中包含所有不符合该规则的单独配置。然后,您将对所有规则生成的所有集合进行并集,以获得所有不符合规则的配置的集合。然后计算该集合的大小,并从集合的大小中减去所有可能的配置。

        困难的部分是以一种可以由您的规则生成的方式定义数据结构,并且可以让集合操作对其进行处理!这是给读者的练习,AKA 我什么都没有。

        【讨论】:

          【解决方案7】:

          我现在唯一能想到的就是构建,如果您可以构建一棵树来定义部件之间的依赖关系,那么您就有一个简单的解决方案。

          sandwitch
          |
          |__Bun(2)__sesame(1)
          |
          |__Mustard(3)
          |
          |__Mayo(2)
          |
          |__Ketchup(2)
          |
          |__Olives(3)
          

          这只是说你有 2 个选项用于包子(包子或不包子) - 1 个用于芝麻(仅当你有包子时 - 表示依赖关系 - 如果这里有 7 则意味着可以存在 7 种类型如果你只有一个面包)

          3 代表芥末……等等

          然后简单地将所有分支的总和相乘。

          【讨论】:

            【解决方案8】:

            可能将问题形式化为k-sat problem。在某些情况下,问题似乎是 NP 完全的,您必须列举所有可能性来检查它们是否满足或不满足所有条件。在其他一些情况下,问题将很容易解决(例如,当需要很少的条件时)。这是一个活跃的研究领域。您可以在 google 学者上找到相关的参考资料。

            在芥末的情况下,您将为芥末类型添加一个二进制条目“mustard_type”并引入条件:not (not mustard and mustard_type) 其中mustard 是芥末的二进制条目。当你选择not mustard时,它会强制默认选择mustard_type == 0。

            对于芝麻的选择,这更明确:not (sesame and not bun)。

            因此,您提出的案例似乎属于 2-sat 系列问题。

            【讨论】:

              猜你喜欢
              • 2021-01-20
              • 1970-01-01
              • 2019-08-26
              • 1970-01-01
              • 1970-01-01
              • 2013-11-01
              • 2012-06-27
              • 2019-03-14
              • 1970-01-01
              相关资源
              最近更新 更多