Algorithm 确定N个输入中的X是否为真的最有效算法

Algorithm 确定N个输入中的X是否为真的最有效算法,algorithm,language-agnostic,performance,Algorithm,Language Agnostic,Performance,这个问题的灵感来源于我昨天正在研究的一个答案 假设我们有N个输入,计算结果为真或假, 确定这些输入中的X是否正确的最有效方法是什么 是吗 注意事项: 输入不在一个数组中,因此如果您将它们转换为一个数组,请考虑任何开销成本 我所说的“最有效”是指最佳平均情况(尽管我也希望看到最佳和最差情况的统计数据) 以下是我昨天遇到的两种方法 1)将变量视为电路的布尔输入,并使用K映射减少它们 起初我认为这是最有效的方法,因为它遵循电路逻辑,但我肯定有第二个想法。随着输入数量的增加,比较的数量呈指数增长 2

这个问题的灵感来源于我昨天正在研究的一个答案

假设我们有N个输入,计算结果为真或假, 确定这些输入中的X是否正确的最有效方法是什么 是吗

注意事项:


  • 输入不在一个数组中,因此如果您将它们转换为一个数组,请考虑任何开销成本
  • 我所说的“最有效”是指最佳平均情况(尽管我也希望看到最佳和最差情况的统计数据)

  • 以下是我昨天遇到的两种方法

    1)将变量视为电路的布尔输入,并使用K映射减少它们

    起初我认为这是最有效的方法,因为它遵循电路逻辑,但我肯定有第二个想法。随着输入数量的增加,比较的数量呈指数增长

    2 inputs:
       1 of 2: if(1 OR 2)
       2 of 2: if(1 AND 2)
    
    3 inputs:
       1 of 3: if(1 OR 2 OR 3)
       2 of 3: if((1 AND 2) OR (1 AND 3) OR (2 AND 3))
       3 of 3: if(1 AND 2 AND 3)
    
    4 inputs:
       1 of 4: if(1 OR 2 OR 3 OR 4)
       2 of 4: if((1 AND 2) OR (1 AND 3) OR (1 AND 4) OR (2 AND 3) OR (2 AND 4) OR (3 AND 4))
       3 of 4: if((1 AND 2 AND 3) OR (1 AND 2 AND 4) OR (1 AND 3 AND 4) OR (2 AND 3 AND 4))
       4 of 4: if(1 AND 2 AND 3 AND 4)
    
    ... etc. ...
    
    最好的情况是好的(
    O(1)
    ),但最坏的情况比

    2)计数器和顺序if语句

    这总是在
    O(n)
    时间内执行,这是可以的,但我希望有更好的情况

    counter = 0
    
    if(input 1)
       counter++
    
    if(input 2)
       counter++
    
    if(input 3)
       counter++
    
    ... etc. ...
    
    if(counter >= X)
       // true
    


    有什么比这两种方法更有效的解决方案呢?

    计算真实值是最快的方法。您自己的计数器将允许大于X的值为真,但问题表明您需要一个特定的值-这不是什么大问题,但如果您至少需要10(但更多是可以的),那么您可以在每次计数器递增后检查计数器并提前中止


    另一方面,如果将标志打包到一个单词中,则有更快的方法计算1。最后,数到1才是正确的选择。顺便说一句,C中的False为零,True为1,因此您可以将它们相加以计算True。

    此版本对于接近零或N的X值非常有效:

    true_counter = 0
    false_counter = 0
    max_false = N - X
    
    if(input 1)
       true_counter++
       if(counter >= X)
          return true
    else
       false_counter++
       if(false_counter > max_false)
          return false
    
    // and so on
    

    关于问题的复杂性
    由于要求精确计数(而不是询问是否至少有x个输入处于打开状态),问题很明显是
    O(n)

    • 我们需要访问每一个输入
    • 每个输入的功独立于n(虽然给定输入的功可能根据输入的特定值而变化,但如果输入数量发生变化,[此输入]的功量不会变化。)
    我们当然可以实现次优算法,例如,在处理每个输入时,(不必要地)访问所有其他输入,使其成为O(n^2)实现,但这当然是愚蠢的

    因此,问题可能是关于……
    使实现更快的技巧
    应该注意的是,虽然可能存在此类技巧,但算法/问题的复杂性仍然顽固地保持在O(n)。

    技巧1:更好地存储输入 不幸的是,问题表明输入来自命名变量,在评估算法的整体性能时,必须考虑输入的任何转换成本[为了允许更快的计数]。尽管这最终取决于底层语言、运行时等,但考虑到转换成本的需要,任何基于备用存储的算法都很可能比保持输入不变的解决方案慢

    技巧2:使评估短路
    其思想是尽快(或不久之后)返回
    false

    • 打开的输入的运行计数大于该数字的X(或者,如果我们正在计算关闭的输入的数目,则当该计数超过(n-X))时)
    • 剩余待测试的输入数量加上打开的输入运行计数小于X(或在计数关闭输入的情况下类似)
    这一技巧相对简单,但计算早期退出测试所需值的额外成本可能会抵消[静态]提前退出带来的收益

    技巧3:使用反向逻辑:计算关闭而不是打开的输入数。(或同时计算两者)。
    这种方法的成本/收益取决于要测试的输入数量(问题的X)和我们可能拥有的关于输入的统计先验知识(给定时间的输入数量是否相对均匀分布,或者我们是否倾向于只有少数输入打开(或关闭))

    为技巧2和技巧3的使用提供了基线。假设我们可以对输入状态的分布做出一些假设,那么对该基线的额外性能改进将由这样的“先验”驱动:在计算输入之前进行的一些快速试探将决定我们应该计算哪个状态(开或关或两者兼而有之),我们应该测试哪些限制等,并将其分支到相应的算法“版本”

    如果我们知道某个给定输入接通或断开的概率,那么也有可能获得额外的收益,因为我们会首先测试最有可能(或最不可能)的输入,以快速获得“短路值”

    关于这些优化算法的最佳情况/最坏情况“复杂性”
    假设

    • 在给定时间打开的输入数是均匀分布的
    • 所有输入在给定时间都有50%的开启变化
    • X在1和n之间随机选择
    技巧#2和#3的组合平均可能是
    O(X/2)
    (我需要做数学运算,但这似乎是正确的)。然而,我认为更明智的做法是谈论相对于X和/或n的
    操作数,而不是误用O符号…
    假设所有操作产生的成本大致相同

    • 伊尼蒂
      if (input1 + input2 + input3 + input4 + ... + inputX > n)
      
      import Data.List (foldl')
      main :: IO ()
      xoutofn :: Num a => (a1 -> Bool) -> [a1] -> a
      xoutofn pred ns = foldl' test 0 (map pred ns) where 
                          test x (True)  = x+1
                          test x (False) = x
      
      main = print $ xoutofn predicate [1 .. 1000] where
          predicate x = x > 500
      
      $ time ./xoutofn
      500
      
      real    0m0.003s
      user    0m0.000s
      sys     0m0.000s