【问题标题】:Pigeon function: where to insert x into a sorted list鸽子函数:将 x 插入排序列表的位置
【发布时间】:2010-12-19 22:30:23
【问题描述】:

假设我有一个排序的列表

{thing1, thing2, thing3, ...}

和一个二进制比较函数,它说明一件事是否应该按该排序顺序排在另一件事之前。 (我说“事物”是因为它们不必是数字;它们可以是任意数据结构。)

我现在寻找一个函数,需要

  • 事物的排序列表,
  • 比较函数,和
  • 一件事

并返回列表中应该插入新事物的第一对相邻事物。 也就是说,返回第一个相邻的对 {a,b} 使得 comp(a,x) 和 comp(x,b) 为真,其中 comp() 是比较函数。

例如,

pigeon[{1,3,5,7}, Less, 4]

应该返回

{3,5}

(编辑:如果给定的事物小于列表的第一个元素 a,则返回 {Null, a}。同样,如果它大于最后一个元素 z,则返回 {z, Null} . 此外,我们需要假设比较函数对于两个相同的元素返回 true(即,它类似于 LessEqual 而不是 Less),或者事物列表不包含被归类的事物。感谢 High Performance Mark 的捕获那个!)

我的第一个想法是使用 Split,然后分别取两个子列表的 Last 和 First。 我会将其发布为答案(或随意击败我),但我想有一种更有效或更优雅的方式。

【问题讨论】:

  • 您是否假设列表的排序与比较功能一致?
  • @Yaroslav,是的。随意编辑问题以使其更清楚。

标签: sorting wolfram-mathematica


【解决方案1】:

使用二分搜索

假设:列表中没有重复的元素。

BinarySearch 有另一种形式:

BinarySearch[l,k,f]
gives the position of k in the list obtained from l by applying f to 
each element in l.  

如果您的列表按上述“f”的结果排序,则可以使用它。

Clear["Global`*"]; Needs["Combinatorica`"];
ret[list_, f_, elem_] := Module[{pos, last},
  pos[l_, e_, g_] := IntegerPart[BinarySearch[l, g[e], g]];
  {
   list[[pos[list, elem, f]]] /. List -> Null,
   If[(last = pos[list, elem, f] + 1) > Length@list, Null, list[[last]]]
  }
 ]  

a = {1, 2, 3, 4, 5};
b = SelectionSort[a, Cos[#1] < Cos[#2] &]

{3, 4, 2, 5, 1}  

Table[{x, N[Cos[x], 2], ret[b, Cos, x]}, 
     {x, 1, 6}]  

{{1,  0.54, {1, Null}}, 
 {2, -0.42, {2, 5}}, 
 {3, -0.99, {3, 4}}, 
 {4, -0.65, {4, 2}}, 
 {5,  0.28, {5, 1}}, 
 {6,  0.96, {1, Null}}
}  

ret[b, Cos, Pi]  
{Null, 3}

【讨论】:

  • 非常感谢您的回答,贝利撒留。是否应该忽略“编辑 II”之上的所有内容?如果是这样,也许可以将其删除,以便答案更简洁,并且保留编辑历史记录。
  • 酷,再次感谢! Nitpick:我的意思是比较函数是从事物交叉事物到布尔值的二进制函数。另外,你确定你需要关于没有重复元素的假设吗?
【解决方案2】:

我认为这是一个很好的解决方案:

Needs["Combinatorica`"]
pigeon[list_, func_, x_] := 
  Join[{Null}, list, {Null}]
    [[ {# - 1/2, # + 1/2}& @
     BinarySearch[list, 0.5, 
      Piecewise[{{0, func[#, x]}, {1, True}}] &] + 1 ]]

给予:

> pigeon[{1, 3, 5, 7}, LessEqual, 0]
{Null, 1}

> pigeon[{1, 3, 5, 7}, LessEqual, 3]
{3, 5}

> pigeon[{1, 3, 5, 7}, LessEqual, 4]
{3, 5}

> pigeon[{1, 3, 5, 7}, LessEqual, 9]
{7, Null}

解释:在BinarySearch里面对列表{1,3,5,7}应用Piecewise函数,检查哪些元素是LessEqual,BinarySearch比找到结束的位置此标记,并返回相关元素。这个实现只使用了 BinarySearch,所以它应该是相当高效的。

此函数可以轻松更改为在第二种情况下返回 {1, 3}。
或者,如果 'x' 可以是 'list' 的一个元素,如下所示:

Needs["Combinatorica`"]
pigeon[list_, func_, 
  x_] := (Join[{Null}, 
    list, {Null}])[[Select[{# - 1/2, #, # + 1/2} &@
      BinarySearch[list, 0, 
       Piecewise[{{0, # == x}, {-1, func[#, x]}, {1, True}}] &], 
     IntegerQ] + 1]]

将给予:

> pigeon[{1, 3, 5, 7}, LessEqual, 3]
{3}

【讨论】:

    【解决方案3】:

    时间不早了,下面是部分指定函数的部分解决方案:

    f[l_List, compFun_Symbol, el_] := 
     Sort[l, compFun] /. {a___, b_ /; compFun[b, el], 
        c_ /; compFun[el, c], d___} -> {b, c}
    

    我冒昧地不要求对提供给函数f 的列表进行排序,因为输入参数包括比较函数。只要 (a) 元素 el 不是 l 的成员,并且 (b) 在 l 中有元素排序到 el 的左侧和右侧,此函数就可以正常工作。它可能不适用于LessEqualGreaterEqual

    如果您想澄清当 (a) 和 (b) 中的一个或两个不满足时,您希望函数返回什么,我很乐意在早上再看看这个。

    编辑:

    我认为这满足了修改后的要求。和以前一样,它不需要输入列表已经排序。

    f2[l_List, compFun_, el_] := Sort[Append[l, el], compFun] /. 
    {a___, el, b___} :> {If[{a}==={}, Null, Last@{a}], If[{b}==={}, Null, First@{b}]}
    

    我会让其他人来判断这个解决方案的优雅和效率(我可以看到后一个方面的一些明显改进)。现在要回去工作了。

    【讨论】:

    • 啊,感谢您指出规格不足!我澄清了这个问题。
    • 第二个答案中的比较函数发生了什么变化? (如果您误解了我的“假设它是一个'小于或等于'函数”,那可能是我的错。我澄清了问题中的说明。)另请参阅我对 belisarius 的评论。这是一个很酷的方法;谢谢!
    • 是的,我以放弃比较运算符的方式解释了您的原始说明。把它放回去应该不会太难。
    • 酷,好的,我对其进行了编辑以将 compFun 重新放入(并在我使用它时稍微压缩了它)。
    【解决方案4】:

    一种可能的方法:

    lst = {1, 3, 5, 7, 9, 11}
    
    {Last@#1, First@#2} & @@ GatherBy[lst, Less[4, #] &]
    

    输出 = {3, 5}

    另外,SplitBy 可以替代 GatherBy。

    【讨论】:

    • 不错!我认为 Split 应该比 Gather 更有效,因为 Split 只考虑相邻元素,因此只需进行一次传递。当然,即使这样也很浪费。一旦找到要返回的相邻对,我们就应该停止遍历列表。
    • 我现在更喜欢 Select,就 O(n) 算法而言。或者也许是模式匹配解决方案;还不确定。 Nitpick:我不确定您的回答是否足够笼统。大概是我的错。请参阅我对高性能标记的评论。
    【解决方案5】:

    这是一个使用 Select(注意第三个参数)的解决方案,它最多通过列表一次,当它找到鸽子时停止:

    pigeon[l_, f_, x_] := Module[{p, r},
      p = Null;
      r = Select[l, If[f[x,#], True, p = #; False]&, 1];
      If[r==={}, {Last@l, Null}, {p, First@r}]]
    

    例子:

    > pigeon[{1,3,5,7}, LessEqual, 4]
    {3, 5}
    
    > pigeon[{1,3,5,7}, LessEqual, 0]
    {Null, 1}
    
    > pigeon[{1,3,5,7}, LessEqual, 9]
    {7, Null}
    

    【讨论】:

      【解决方案6】:

      这是另一个二分搜索答案:

      (* Helper for pigeon. Additional arguments a and b give current bounds on the 
         indices of the pigeonhole. *)
      pigeon0[l_, f_, x_, a_, b_] := Which[
        b-a==1,                    {l[[a]], l[[b]]},
        f[x, l[[Floor[(a+b)/2]]]], pigeon0[l, f, x, a, Floor[(a+b)/2]],
        True,                      pigeon0[l, f, x, Floor[(a+b)/2], b]]
      
      pigeon[l_, f_, x_] := Which[
        f[x, First@l],                 {Null, First@l},
        f[Last@l, x] && !f[x, Last@l], {Last@l, Null},
        True,                          pigeon0[l, f, x, 1, Length@l]]
      

      我试过这样:

      > l = Sort@RandomReal[{0,1}, {10^6}];
      > pigeon[l, LessEqual, .5]
      {0.4999991874459364, 0.5000000938493356}
      

      这与我的其他答案(带有 Select 的答案)相匹配,但要快得多。

      其他例子:

      > pigeon[{1,3,5,7}, LessEqual, 4]
      {3, 5}
      
      > pigeon[{1,3,5,7}, LessEqual, 0]
      {Null, 1}
      
      > pigeon[{1,3,5,7}, LessEqual, 9]
      {7, Null}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-26
        • 2017-06-14
        相关资源
        最近更新 更多