我怎样才能有效地从一堆袜子配对?

昨天,我正在从干净的洗衣店配对袜子,发现我的方法效率不高。我正在做一个天真的搜索——挑选一只袜子并“迭代”一堆以找到它的配对。这需要平均迭代n/2*n/4=n2/8只袜子。

作为一名计算机科学家,我在想我能做什么?排序(根据大小/颜色/…)当然想到了实现O(NlogN)解决方案。

哈希或其他不到位的解决方案不是一种选择,因为我无法复制我的袜子(尽管如果可以的话可能会很好)。

所以,问题基本上是:

给定一堆n双袜子,包含2n个元素(假设每只袜子正好有一对匹配的袜子),用最多对数的额外空间有效配对它们的最佳方法是什么?(如果需要,我相信我可以记住这个数量的信息。)

我希望你能回答以下几个方面的问题:

  • 对于大量袜子的通用理论解决方案。
  • 袜子的实际数量没有那么多,我相信我和我的配偶不会超过30双。(而且区分我的袜子和她的袜子很容易;这也可以用吗?)
  • 它是否等同于元素区别性问题
433331 次浏览

我所做的就是拿起第一只袜子,把它放下(比如,放在洗衣碗的边缘)。然后我拿起另一只袜子,检查它是否和第一只袜子一样。如果是,我把它们都拿开。如果不是,我把它放在第一只袜子旁边。然后我拿起第三只袜子,把它和前两只袜子比较(如果它们还在那里)。等等。

这种方法可以很容易地在数组中实现,假设“删除”袜子是一种选择。实际上,你甚至不需要“删除”袜子。如果你不需要对袜子进行排序(见下文),那么你可以移动它们并最终得到一个数组,该数组中所有袜子成对排列。

假设袜子的唯一操作是比较相等,这个算法基本上仍然是一个n2算法,尽管我不知道平均情况(从来没有学过计算)。

排序当然可以提高效率,尤其是在现实生活中,你可以很容易地在另外两只袜子之间“插入”一只袜子。在计算中,同样的结果可以用树来实现,但这是额外的空间。当然,我们又回到了NlogN(或者更多,如果有几只袜子根据排序标准是相同的,但不是来自同一双)。

除此之外,我想不出任何东西,但这种方法似乎在现实生活中非常有效。:)

理论极限是O(n),因为你需要触摸每只袜子(除非有些已经以某种方式配对)。

您可以使用基数排序实现O(n)。您只需要为存储桶选择一些属性。

  1. 首先你可以选择(她的,我的)-把它们分成两堆,
  2. 然后使用颜色(可以对颜色有任何顺序,例如按颜色名称按字母顺序排列)-按颜色将它们分成堆(记住保留步骤1中同一堆中所有袜子的初始顺序),
  3. 然后袜子的长度,
  4. 然后是纹理,……

如果你可以选择有限数量的属性,但足够的属性可以唯一标识每对,你应该用O(k*n)来完成,如果我们可以考虑k是有限的,那就是O(n)。

案例1:所有的袜子都是一样的(顺便说一句,这就是我在现实生活中所做的)。

选择其中任何两个做一对。恒定时间。

案例2:有恒定数量的组合(所有权、颜色、大小、纹理等)。

使用基数排序。这只是线性时间,因为不需要比较。

案件3:预先不知道组合的数量(一般情况)。

我们必须进行比较以检查两只袜子是否成对。选择一个O(n log n)基于比较的排序算法。

然而,在现实生活中,当袜子数量相对较少(恒定)时,这些理论上最优的算法不会很好地工作。它可能比顺序搜索需要更多的时间,理论上需要二次时间。

成本:移动袜子->高,在线查找/搜索袜子->小

我们想要做的是减少移动次数,并通过搜索次数进行补偿。此外,我们可以利用智人的多维度环境在共享缓存中容纳更多的东西。

X=你的,Y=你的配偶

从所有袜子的A堆:

选择两只袜子,将相应的X袜子放在X线上,并将Y袜子放在Y线上的下一个可用位置。

直到A是空的。

对于每一行X和Y

  1. 选择第一只袜子,沿着这条线搜索,直到找到相应的袜子。

  2. 放入相应的成品袜子线。

  3. 可选当您搜索该行并且您正在查看的当前袜子与前一个相同时,对这些袜子执行步骤2。

可选的第一步,你从该行拿起两只袜子,而不是两只,因为缓存内存足够大,我们可以快速识别任何一只袜子是否与你正在观察的行上的当前袜子匹配。如果你有幸有三只手臂,考虑到主题的内存足够大,你可能同时解析三只袜子。

直到X和Y都为空。

成交

然而,由于这与选择排序具有类似的复杂性,因此由于I/O(移动袜子)和搜索(在行中搜索袜子)的速度,所花费的时间要少得多。

当你拿起一只袜子时,把它放在一个地方。然后你拿起的下一只袜子,如果它和第一只袜子不匹配,就把它放在第一只袜子旁边。如果匹配,就有一对。这样,有多少组合并不重要,你拿起的每只袜子只有两种可能性——要么它已经在你的袜子数组中匹配了,要么没有,这意味着你把它添加到数组中的一个地方。

这也意味着你几乎肯定不会在数组中拥有所有的袜子,因为袜子会在匹配时被移除。

已经提出了排序解决方案,但是排序有点太多了:我们不需要顺序;我们只需要平等团体

所以散列就足够了(而且更快)。

  1. 对于每种颜色的袜子,形成一堆。迭代输入篮子并把它们分发到颜色堆上中的所有袜子。
  2. 在每个桩和按其他度量分布(例如模式)上迭代到第二组桩
  3. 递归应用此方案直到你把所有的袜子都分发到非常小的堆,你可以立即视觉处理

这种递归哈希分区实际上是由SQL服务器在需要对巨大数据集进行哈希连接或哈希聚合时完成的。它将其构建输入流分布到许多独立的分区中。该方案线性扩展到任意量的数据和多个CPU。

如果你能找到一个分布键(散列键),每个桶都足够小,可以非常快速地处理,你就不需要递归分区。不幸的是,我认为袜子没有这样的属性。

如果每只袜子都有一个名为“PairID”的整数,则可以根据PairID % 10(最后一位)轻松将它们分配到10个桶中。

我能想到的最好的现实世界分区是创建一个矩形桩:一个维度是颜色,另一个是图案。为什么是矩形?因为我们需要O(1)对桩的随机访问。(3D长方体也可以,但不太实用。)


更新时间:

那么并行呢?多人能更快地匹配袜子吗?

  1. 最简单的并行化策略是让多个工作人员从输入篮中取出袜子,然后将袜子放在堆上。这只能扩大到这么多——想象一下100人在10个堆上打架。同步成本(表现为手碰撞和人类交流)破坏效率和加速(见通用伸缩性定律!)。这容易发生死锁吗?不,因为每个工作人员一次只需要访问一堆。只有一个“锁”,就不可能有死锁。Livelocks可能是可能的,这取决于人类如何协调对堆上的访问。他们可能只是使用随机退避,就像网卡在物理层面上这样做,以确定什么卡可以独家访问网线。如果它适用于NIC,它也应该适用于人类。
  2. 如果每个工人都有自己的一套桩,它几乎无限期地扩展。然后,工人可以从输入篮子中取出大块袜子(很少有争用,因为他们很少这样做),并且他们在分发袜子时根本不需要同步(因为它们有线程本地桩)。最后,所有工人都需要联合他们的桩集。我相信如果工人形成聚合树,这可以在O(log(工人计数*每个工人的桩数))中完成。

那么元素区别性问题呢?正如文章所述,元素独特性问题可以在O(N)中解决。袜子问题也是如此(如果你只需要一个分布步骤(我提议多个步骤只是因为人类不擅长计算——如果你在md5(color, length, pattern, ...)上分布,一个步骤就足够了,即所有属性的完美哈希))。

显然,一个人不能比O(N)更快,所以我们已经到达了最优下限

尽管输出不完全相同(在一种情况下,只是一个布尔值。在另一种情况下,袜子对),但渐近复杂度是相同的。

非算法答案,但当我这样做时“高效”:

  • 步骤1)丢弃所有现有的袜子

  • 第2步)转到沃尔玛并按10-n包的包购买它们白色和M包黑色。日常生活中不需要其他颜色生活。

然而,有时,我不得不再次这样做(丢失的袜子,损坏的袜子等),我讨厌经常丢弃完美的袜子(我希望他们继续销售相同的袜子参考!),所以我最近采取了不同的方法。

算法答案:

考虑一下,如果你只为第二堆袜子画一只袜子,就像你正在做的那样,你在天真的搜索中找到匹配袜子的几率相当低。

  • 所以随机挑选五个,记住它们的形状或长度。

为什么是五个?通常人类擅长记住工作记忆中的五到七个不同元素-有点像人类相当于RPN堆栈-五是安全的默认值。

  • 从2n-5的堆栈中拿起一个。

  • 现在在你画的五个里面寻找一个匹配(视觉模式匹配-人类擅长用一个小堆栈),如果你没有找到一个,然后把它添加到你的五个。

  • 继续从堆栈中随机挑选袜子,并与你的5+1袜子进行比较。随着堆栈的增加,它会降低你的表现,但会提高你的赔率。快得多。

随意写下公式来计算您必须抽取多少样本才能获得50%的匹配几率。IIRC这是一个超几何定律。

我每天早上都这样做,很少需要超过三个平局-但我有n双类似的(大约10双,给或拿走丢失的)m形状的白袜子。现在你可以估计我的一堆股票的大小:-)

顺便说一句,我发现每次我需要一双袜子时,将所有袜子分类的交易成本总和远低于一次并捆绑袜子。准时制效果更好,因为这样你就不必捆绑袜子,而且边际回报也在递减(也就是说,当你在洗衣店的某个地方寻找那两三双袜子时,你需要完成匹配你的袜子,你会浪费时间)。

由于人脑的架构与现代CPU完全不同,这个问题没有实际意义。

人类可以使用“找到匹配的对”这一事实来赢得CPU算法,这对于一个不太大的集合来说可能是一个操作。

我的算法:

spread_all_socks_on_flat_surface();while (socks_left_on_a_surface()) {// Thanks to human visual SIMD, this is one, quick operation.pair = notice_any_matching_pair();remove_socks_pair_from_surface(pair);}

至少这是我在现实生活中使用的,我发现它非常有效。缺点是它需要一个平坦的表面,但它通常很丰富。

作为一个实用的解决方案:

  1. 快速制作成堆易于区分的袜子。(按颜色说)
  2. 对每一叠袜子进行快速排序,并使用袜子的长度进行比较。作为一个人类,你可以相当快速地决定使用哪只袜子来分区,以避免最坏的情况。(你可以看到多只袜子并行,利用这一点对你有利!)
  3. 停止排序桩时,他们达到了一个阈值,在这个阈值上,你很舒服地立即找到现货对和无法支付的袜子

如果你有1000只袜子,有8种颜色和平均分布,你可以在c*n时间内每125只袜子做4堆。以5只袜子为阈值,你可以在6次运行中对每堆袜子进行排序。(计算2秒将袜子扔到右边的那堆,你将花费不到4个小时。)

如果你只有60只袜子,3种颜色和2种袜子(你的/你妻子的),你可以在1次运行中对每10只袜子进行分类(再次阈值=5)。

最初的桶排序将加快你的过程,因为它在c*n时间内将你的n只袜子分成k个桶,所以你只需要做c*n*log(k)个工作。(不考虑阈值)。所以总的来说,你做的是n*c*(1 + log(k))个工作,其中c是把袜子扔到一堆上的时间。

与任何c*x*n + O(1)方法相比,这种方法将是有利的,大约只要log(k) < x - 1


在计算机科学中,这可能很有帮助:我们有一个n事情的集合,它们上有一个顺序(长度)和一个等价关系(额外的信息,例如袜子的颜色)。等价关系允许我们对原始集合进行分区,在每个等价类中我们的顺序仍然得到保持。事情到它的等价类的映射可以在O(1)中完成,所以只需要O(n)来将每个项目分配给一个类。现在我们已经使用了额外的信息,可以以任何方式对每个类进行排序。优点是数据集已经明显小了。

该方法也可以嵌套,如果我们有多个等价关系->制作颜色堆,而不是在纹理上的每个堆分区内,而不是在长度上排序。任何等价关系创建一个具有超过2个元素的分区,其大小大约是偶数,这将带来比排序更快的速度提高(前提是我们可以直接将袜子分配给它的堆),并且排序可以在较小的数据集上非常快地发生。

这是基于比较的模型中的Omega(n log n)下限。(唯一有效的操作是比较两只袜子。)

假设你知道你的2n只袜子是这样排列的:

p1 p2 p3… pn pF(1) pF(2)… pf(n)

其中f是集合{1,2,…, n}的未知排列。知道这并不能使问题变得更难。有n个!可能的输出(前半部分和后半部分之间的匹配),这意味着你需要log(n!)=Omega(n log n)比较。这可以通过排序获得。

由于你对元素区别性问题的联系感兴趣:证明元素区别性的ω(n log n)界更难,因为输出是二进制的yes/no。在这里,输出必须是匹配的,并且可能输出的数量足以得到一个像样的界。然而,有一个变体与元素区别性有关。假设你有2n只袜子,想知道它们是否可以唯一配对。你可以通过发送(a1, a2,…, an)到(a1, a1, a2,…, an)来从ED得到约简。(顺便说一句,ED的硬度证明非常有趣,通过拓扑。)

我认为如果只允许相等测试,应该有一个Omega(n2)绑定到原始问题。我的直觉是:考虑一个在测试后添加一条边的图,并认为如果图不稠密,输出不是唯一确定的。

如果您可以将一对袜子抽象为键本身,将其另一对抽象为值,我们可以将哈希用于我们的杠杆。

  1. 在你身后的地板上做两个想象的部分,一个给你,另一个给你的配偶。

  2. 从袜子堆里拿一个。

  3. 现在把袜子放在地板上,一个接一个,下面的规则。

    • 确定袜子是你的或她的,看看地板上的相关部分。

    • 如果你能在地板上找到一对,把它捡起来,打个结,或者把它们夹起来,或者在你找到一对后做任何你想做的事情,把它放在一个篮子里(从地板上取下)。

    • 将其放在相关部分。

  4. 重复3次,直到所有的袜子都从一堆。


解释:

哈希和抽象

抽象是一个非常强大的概念,已被用于改善用户体验(用户体验)。现实生活中与计算机交互中的抽象示例包括:

  • 用于在GUI(图形用户交互界面)中导航以访问地址而不是键入实际地址以导航到位置的文件夹图标。
  • GUI滑块用于控制各种级别的音量、文档滚动位置等。

哈希或其他不到位的解决方案不是一种选择,因为我无法复制我的袜子(尽管如果可以的话可能会很好)。

我相信提问者正在考虑应用散列,以便在放置它们之前应该知道任何一双袜子的插槽。

这就是为什么我建议抽象出一只放在地板上的袜子作为散列键本身(因此不需要复制袜子)。

如何定义我们的哈希键?

如果有多双相似的袜子,下面的关键定义也可以。也就是说,假设有两双黑人袜子PairA和PairB,每只袜子都被命名为PairA-L、PairA-R、PairB-L、PairB-R。所以PairA-L可以和PairB-R配对,但PairA-L和PairB-L不能配对。

假设任何袜子都可以被唯一地识别,

Attribute[Gender] + Attribute[Colour] + Attribute[Material] + Attribute[Type1] + Attribute[Type2] + Attribute[Left_or_Right]

这是我们的第一个哈希函数。让我们用一个简短的符号来表示这个h1(G_C_M_T1_T2_LR)h1(x)不是我们的位置键。

另一个消除Left_or_Right属性的哈希函数是h2(G_C_M_T1_T2)。第二个函数h2(x)是我们的位置键!(用于您身后地板上的空间)。

  • 要定位插槽,请使用h2(G_C_M_T1_T2)。
  • 找到插槽后,使用h1(x)检查它们的哈希值。如果它们不匹配,则有一对。否则将袜子扔进同一个插槽。

注意:由于我们在找到一对后删除了一对,因此可以安全地假设最多只有一个槽具有唯一的h2(x)或h1(x)值。

如果我们每个袜子都有一个匹配的配对,那么使用h2(x)来查找位置,如果没有配对,则需要检查,因为可以安全地假设它们是一对。

为什么把袜子放在地板上很重要

让我们考虑一个场景,袜子堆在一起(最坏的情况)。这意味着我们别无选择,只能进行线性搜索以找到一对。

将它们分散在地板上可以提供更多可见性,从而提高发现匹配袜子(匹配散列键)的机会。在步骤3中,当一只袜子放在地板上时,我们的大脑已经下意识地记录了这个位置。-因此,如果这个位置在我们的内存中可用,我们可以直接找到匹配的对。-如果位置不被记住,不要担心,那么我们总是可以恢复到线性搜索。

为什么从地板上取下这对很重要?

  • 短期人类记忆在需要记住的项目较少时效果最好。因此增加了我们诉诸散列来发现配对的可能性。
  • 当使用线性搜索对时,它还将减少要搜索的项目数量。

分析

  1. 案例1:最坏的情况是Derpina无法记住或直接使用哈希技术发现地板上的袜子。Derp对地板上的物品进行线性搜索。这并不比在一堆中迭代找到一对更糟糕。
    • 比较的上限:O(n^2)。
    • 比较的下限:(n/2)。(当Derpina拿起的每只袜子都是前一双)。
  2. 案例2:Derp记住他放在地板上的每只袜子的位置,每只袜子只有一双。
    • 比较的上限:O(n/2)。
    • 比较的下限:O(n/2)。

我说的是比较操作,从一堆中挑选袜子必然是n次操作。所以一个实际的下限是n次迭代,n/2次比较。

加快速度

为了达到一个完美的分数,所以Derp得到O(n/2)比较,我会推荐Derpina,

  • 花更多的时间在袜子上熟悉它。是的,这意味着花更多的时间在德普的袜子上。
  • 玩记忆游戏,比如在网格中发现对子,可以提高短期记忆表现,这是非常有益的。

这是否等同于元素区分问题?

我建议的方法是用于解决元素独特性问题的方法之一,您可以将它们放在哈希表中并进行比较。

考虑到你的特殊情况,只有一个确切的对,它已经变得非常等价于元素不同问题。因为我们甚至可以对袜子进行排序并检查相邻的袜子是否有对(EDP的另一个解决方案)。

但是,如果对于给定的袜子存在多于一对的可能性,则它会偏离EDP。

这就是我实际做的,对于p双袜子(n=2p双袜子):

  • 从袜子堆里随便拿一只袜子。
  • 对于第一只袜子,或者如果所有先前选择的袜子都已配对,只需将袜子放入您面前未配对袜子“数组”的第一个“槽”中。
  • 如果您有一个或多个选定的未配对袜子,请将当前袜子与数组中所有未配对的袜子进行检查。
    • 在构建数组时,可以将袜子分为一般类别或类型(白色/黑色,脚踝/船员,运动/连衣裙),并“向下钻取”以仅比较同类。
    • 如果找到可接受的匹配,请将两个袜子放在一起并将其从数组中删除。
    • 如果没有,则将当前袜子放入数组中的第一个开放槽中。
  • 对每只袜子重复。

这个方案最坏的情况是每双袜子都不同,必须完全匹配,而且你挑选的第一双n/2袜子都是不同的。这是你的O(n2)场景,这是非常不太可能。如果袜子t的唯一类型的数量小于袜子对的数量p=n/2,并且每种类型的袜子都足够相似(通常是在与磨损相关的术语中),以至于该类型的任何袜子都可以与任何其他袜子配对,那么正如我在上面推断的,你必须比较的最大袜子数量是t,之后你拉的下一只匹配其中一只未配对的袜子。这种情况在普通袜子抽屉里比最坏情况下更有可能发生,并将最坏情况的复杂性降低到O(n*t),通常是t<

考虑一个大小为“N”的哈希表。

如果我们假设正态分布,那么至少有一个袜子映射到一个桶的“插入”的估计数量是NlogN(即,所有桶都满了)

我推导出这是另一个谜题的一部分,但我很乐意被证明是错误的。这是我的博客文章在同一

让'N'对应于你拥有的袜子的独特颜色/图案数量的近似上限。

一旦你有一个碰撞(a. k. a:匹配)只需删除一双袜子。对下一批NlogN袜子重复相同的实验。它的美妙之处在于,你可以进行NlogN并行比较(碰撞分辨率),因为人类思维的工作方式. :-)

List<Sock> UnSearchedSocks = getAllSocks();List<Sock> UnMatchedSocks = new list<Sock>();List<PairOfSocks> PairedSocks = new list<PairOfSocks>();
foreach (Sock newSock in UnsearchedSocks){Sock MatchedSock = null;foreach(Sock UnmatchedSock in UnmatchedSocks){if (UnmatchedSock.isPairOf(newSock)){MatchedSock = UnmatchedSock;break;}}if (MatchedSock != null){UnmatchedSocks.remove(MatchedSock);PairedSocks.Add(new PairOfSocks(MatchedSock, NewSock));}else{UnmatchedSocks.Add(NewSock);}}

从你的问题中可以清楚地看出,你在洗衣方面没有太多的实际经验:)。你需要一个算法,它可以很好地处理少量不可兑换的袜子。

到目前为止的答案并没有很好地利用我们人类的模式识别能力。Set游戏提供了如何做好这一点的线索:将所有袜子放在一个二维空间中,这样你就可以很好地识别它们并且很容易用手够到它们。这将你限制在大约120*80厘米的区域内。从那里选择你认识的袜子并将它们移除。将额外的袜子放在空闲空间中并重复。如果你为穿着容易识别的袜子的人洗衣服(想想小孩子),你可以通过首先选择这些袜子来进行基数排序。只有当单袜子数量较少时,此算法才能有效

袜子,无论是真实的袜子还是类似的数据结构,都将成对提供。

最简单的答案是,在允许这对袜子被分离之前,应该初始化这对袜子的单个数据结构,其中包含一个指向左右袜子的指针,从而使袜子能够直接或通过它们的袜子被引用。

这通过使用抽象层将其删除来解决任何计算配对问题。

将同样的想法应用于袜子配对的实际问题,显而易见的答案是:永远不要让你的袜子不配对。袜子是成对提供的,作为一对放在抽屉里(也许是通过将它们捆绑在一起),作为一对穿着。但是,可能不配对的地方是在洗衣机里,所以所需要的只是一个物理机制,让袜子保持在一起并有效地洗涤。

有两种物理可能性:

对于每个袜子都有一个指针的一对物体,我们可以有一个布袋,用来把袜子放在一起。这似乎是巨大的开销。

但是对于每只袜子都要保持对另一只袜子的引用,有一个巧妙的解决方案:一个popper(如果你是美国人,或者一个“按扣”),比如:

http://www.aliexpress.com/compare/compare-invisible-snap-buttons.html

然后你所要做的就是在你脱下袜子并把它们放在洗衣篮里之后立即将它们扣在一起,并且再次消除了需要将袜子与“配对”概念的物理抽象配对的问题。

这个问题实际上具有深刻的哲学意义。本质上,它是关于人类解决问题的能力(我们大脑的“湿件”)是否等同于算法所能完成的事情。

袜子排序的一个明显算法是:

Let N be the set of socks that are still unpaired, initially emptyfor each sock s taken from the dryerif s matches a sock t in Nremove t from N, bundle s and t together, and throw them in the basketelseadd s to N

这个问题中的计算机科学是关于步骤的

  1. "if s与n中的sock t配对"。我们能多快"记住"到目前为止我们所看到的?
  2. “从N中删除t”和“将s添加到N”。跟踪我们迄今为止看到的东西有多昂贵?

人类将使用各种策略来实现这些。人类记忆是联想的,类似于哈希表的东西,其中存储值的特征集与相应的值本身配对。例如,“红色汽车”的概念映射到一个人能够记住的所有红色汽车。拥有完美记忆的人有完美的映射。大多数人在这方面(和大多数其他人)都不完美。关联地图的容量有限。映射可能在各种情况下(啤酒太多了)不存在,被错误记录(“我以为她的名字是贝蒂,不是Nettie”),或者即使我们观察到真相已经改变,也永远不会被覆盖(“爸爸的车”唤起“橙色火鸟”当我们实际上知道他用红色Camaro交换了它)。

就袜子而言,完美的回忆意味着看着袜子s总是能产生它的兄弟姐妹t的记忆,包括足够的信息(它在熨衣板上的位置)来在恒定的时间内找到t。一个有摄影记忆的人在恒定的时间内完成了1和2,没有失败。

记忆力不太好的人可能会使用一些常识性的等价类,这些等价类基于他有能力追踪的特征:尺寸(爸爸、妈妈、婴儿)、颜色(绿色、红色等)、图案(Argyle、普通等)、风格(足球、及膝高等)。因此,熨烫板将被分成不同的类别。这通常允许通过记忆在恒定时间内定位类别,但随后需要在类别“桶”中进行线性搜索。

完全没有记忆或想象力的人(对不起)会把袜子放在一堆里,然后对整堆进行线性搜索。

整洁的人可能会按照某人的建议使用数字标签对。这为总排序打开了大门,它允许人类使用与CPU完全相同的算法:二进制搜索、树、哈希等。

所以“最佳”算法取决于运行它的湿件/硬件/软件的质量,以及我们通过强加对对的总顺序来“作弊”的意愿。当然,“最佳”meta-算法是雇佣世界上最好的袜子分拣员:一个人或机器,可以获取并快速将大量N个袜子属性集存储在1-1关联内存中,并进行恒定的时间查找、插入和删除。这样的人和机器都可以采购。如果你有一只,你可以在O(N)时间内将所有袜子配对N对,这是最佳的。总订单标签允许您使用标准散列来获得与人或硬件计算机相同的结果。

我想出了另一个解决方案,它不会承诺更少的操作,也不是更少的时间消耗,但应该尝试一下,看看它是否可以是一个足够好的启发式方法,在大量的袜子配对中提供更少的时间消耗。

前提条件:不能保证有相同的袜子。如果它们是相同的颜色,并不意味着它们有相同的尺寸或图案。袜子是随机洗牌的。袜子可以有奇数个(有些缺失,我们不知道有多少)。准备记住一个变量“index”并将其设置为0。

的结果将有一个或两个堆:1.“匹配”和2.“缺失”

启发式:

  1. 找到最有特色的袜子。
  2. 找到它的匹配。
  3. 如果没有匹配,把它放在“丢失”的堆上。
  4. 重复从1.直到没有更多的最独特的袜子。
  5. 如果少于6只袜子,则转到11只。
  6. 盲目地将所有袜子与邻居配对(不要打包)
  7. 找到所有匹配的对,打包并将打包的对移动到“匹配”堆;如果没有新的匹配项-将“索引”增加1
  8. 如果“index”大于2(这可能是取决于袜子的值数量,因为有更多的袜子有更少的机会将它们盲目配对)转到11
  9. 把剩下的洗牌
  10. 转到1
  11. 忘记“索引”
  12. 挑只袜子
  13. 找到它的配对
  14. 如果袜子没有一双,把它移到“丢失”的那堆
  15. 如果匹配找到配对,打包配对并将其移动到“匹配”堆
  16. 如果还有更多,那么一只袜子去12
  17. 如果只剩下一个,去14
  18. 满意的微笑:)

此外,还可以添加检查损坏的袜子,就像移除袜子一样。它可以插入2到3之间,13到14之间。

我期待着听到任何经验或更正。

做一些预处理怎么样?我会在每只袜子上缝一个标记或身份证号,以每双袜子都有相同的标记/身份证号的方式。这个过程可能在你每次购买一双新袜子时进行。然后,你可以做一个基数排序来获得O(n)的总成本。为每个标记/身份证号找到一个地方,然后逐一挑选所有袜子并将它们放在正确的地方。

为了说明从一堆袜子配对的效率,我们必须首先定义机器,因为配对不是通过图灵或随机存取机器完成的,这通常用作算法分析的基础。

这台机器

该机器是对称为人类的现实世界元素的抽象。它能够通过一双眼睛从环境中读取信息。我们的机器模型能够使用两条手臂来操纵环境。逻辑和算术运算是使用我们的大脑计算的(希望是;-))。

我们还必须考虑可以用这些仪器执行的原子操作的内在运行时间。由于物理限制,由手臂或眼睛执行的操作具有非恒定的时间复杂度。这是因为我们不能用手臂移动无休止的一大堆袜子,眼睛也看不到无休止的一大堆袜子上的顶部袜子。

然而,机械物理学也给我们带来了一些好处。我们不仅限于用一只手臂移动一只袜子。我们可以一次移动整整两只袜子。

因此,根据前面的分析,以下操作应按降序使用:

  • 逻辑和算术运算
  • 环境读数
  • 环境改造

我们还可以利用这样一个事实,即人们只有非常有限的袜子数量。因此,环境改造可以涉及所有的袜子。

该算法

以下是我的建议:

  1. 把袜子堆在地板上。
  2. 通过看地板上的袜子找到一双。
  3. 从2开始重复,直到没有配对。
  4. 从1开始重复,直到地板上没有袜子。

操作4是必要的,因为当袜子在地板上传播时,一些袜子可能会隐藏其他袜子。以下是算法的分析:

的分析

该算法很可能会终止,这是因为在步骤2中找不到一双袜子。

对于以下配对n对袜子的运行时分析,我们假设至少有一半2n的袜子在步骤1之后没有隐藏。所以在平均情况下我们可以找到n/2对。这意味着循环第4步被执行O(log n)次。第2步被执行O(n^2)次。所以我们可以得出结论:

  • 该算法涉及O(ln n + n)环境修改(步骤1O(ln n)加上从地板上挑选每双袜子)
  • 该算法涉及第2步的O(n^2)环境读取
  • 该算法涉及O(n^2)逻辑和算术运算,用于在步骤2中将袜子与另一只袜子进行比较

所以我们的总运行时复杂度为#0,其中rw分别是合理数量的袜子的环境读取和环境写入操作的因素。逻辑和算术运算的成本被省略,因为我们假设需要恒定的逻辑和算术运算来决定2只袜子是否属于同一对。这在每种情况下都可能不可行。

这是一个错误的问题。正确的问题是,为什么我要花时间整理袜子?当你用你选择的X个货币单位来评估你的空闲时间时,每年花费多少钱?

通常情况下,这不仅仅是任何空闲时间,这是早上空闲时间,你可以在床上度过,或者喝咖啡,或者早点离开,不要被堵车。

退后一步,思考解决问题的方法通常是好的。

还有一个办法!

找一只你喜欢的袜子。考虑所有相关的特征:不同光照条件下的颜色、整体质量和耐用性、不同气候条件下的舒适度和气味吸收。同样重要的是,它们在储存时不应该失去弹性,所以天然织物很好,它们应该用塑料包装。

如果左脚和右脚的袜子没有区别会更好,但这不是关键。如果袜子是左右对称的,找到一双是O(1)操作,排序袜子是近似O(M)操作,其中M是你家里乱扔袜子的地方的数量,理想情况下是一些小的常数。

如果你选择了一双左右袜子不同的花哨的袜子,对左右脚桶进行完整的桶排序需要O(N+M),其中N是袜子的数量,M与上面相同。其他人可以给出找到第一对的平均迭代公式,但通过盲目搜索找到一对的最坏情况是N/2+1,这对于合理的N来说变得不太可能。

因此,实现O(1)袜子配对效率的算法(假设对称袜子)是:

  1. 你需要估计你余生需要多少双袜子,或者直到你退休,搬到更温暖的气候,再也不用穿袜子。如果你还年轻,你还可以估计需要多长时间才能在我们的家中拥有袜子分类机器人,整个问题就变得无关紧要了。

  2. 你需要找出如何批量订购你选择的袜子,它的成本是多少,他们是否交货。

  3. 订购袜子!

  4. 扔掉你的旧袜子。

第三步是比较购买同样数量的袜子的成本,一次买几双可能更便宜的袜子,然后加上分拣袜子的成本,但相信我的话:批量购买更便宜!此外,存储中的袜子以股价通胀的速度增值,这比你在许多投资中获得的要多。此外,还有存储成本,但袜子在壁橱顶层的空间并不大。

问题解决了。所以,买双新袜子,扔掉旧袜子,在知道你余生每天都在节省金钱和时间后,快乐地生活。

现实世界方法:

尽快地从未分类的袜子堆中取出袜子,一次一个地放在你前面的成堆的地方。成堆的袜子应该合理地排列,所有的袜子都指向同一个方向;成堆的数量受到你容易到达的距离的限制。选择放袜子的一堆应该-尽可能快-通过将袜子放在一堆看起来像袜子的堆上;偶尔的I型(将袜子放在不属于它的堆上)或II型(当存在一堆类似袜子时将袜子放在自己的堆上)错误可以容忍-最重要的考虑因素是速度

一旦所有的袜子都成堆了,迅速穿过多袜子堆,创建成对并将其移除(这些是前往抽屉的)。如果有不匹配的袜子在堆中,将它们重新堆积到最佳状态(在尽可能快的约束范围内)。当所有的多袜子堆都处理完毕后,将剩余的由于类型II错误而未配对的可配对袜子配对起来。呼,你完成了--我有很多袜子,直到很大一部分脏了才洗。另一个实用的注意事项:我将一双袜子的顶部翻转到另一双上面,利用它们的弹性特性,所以它们在被运到抽屉和抽屉里时保持在一起。

我已经采取了一些简单的步骤,将我的努力减少到一个花费O(1)时间的过程中。

通过减少我对两种袜子中的一种的投入(休闲用的白袜子,工作用的黑袜子),我只需要确定我手里有哪两只袜子。(从技术上讲,因为它们从来没有一起洗过,我把这个过程减少到O(0)时间。)

为了找到合适的袜子,需要一些前期的努力,并购买足够的数量,以消除对现有袜子的需求。正如我在需要黑色袜子之前所做的那样,我的努力很小,但里程可能会有所不同。

这样的前期工作在非常流行和有效的代码中已经多次出现。例子包括#DEFINE'ing pi到几个小数(还有其他例子,但这是现在想到的)。

创建一个哈希表,将用于不匹配的袜子,使用模式作为哈希。逐个迭代袜子。如果袜子在哈希表中有匹配的模式,则将袜子从表中取出并制作一对。如果袜子没有匹配,则将其放入表中。

在我攻读计算机科学博士学位期间,我经常思考这个问题。我想出了多种解决方案,这取决于区分袜子的能力,从而尽可能快地找到正确的袜子。

假设查看袜子的成本和记住它们独特的模式是微不足道的(ε)。那么最好的解决方案就是简单地将所有袜子扔到一张桌子上。这包括以下步骤:

  1. 将所有袜子扔到一张桌子上(1)并创建一个hashmap{模式:位置}(ε)
  2. 剩下的袜子(n/2):
    1. 随便拿一只袜子(1)
    2. 查找相应袜子的位置(ε)
    3. 取回袜子(1)并储存一对

这确实是最快的可能性,并且以n+1=O(n)的复杂度执行。但它假设你完美地记住了所有模式……在实践中,情况并非如此,我的个人经验是,有时你在第一次尝试时找不到匹配的对:

  1. 把所有的袜子都扔到桌子上(1)
  2. 剩下的袜子(n/2):
    1. 随便拿一只袜子(1)
    2. 如果不是配对(1/P):
      1. 找到类似图案的袜子
      2. 拿袜子比较(1)
      3. 如果OK,商店配对

现在这取决于我们找到匹配袜子的能力。如果你有一双深色/灰色或白色的运动袜,它们的图案通常非常相似,这一点尤其如此!让我们承认你找到相应袜子的概率为P。在找到相应的袜子形成一双之前,你平均需要1/P次尝试。整体复杂性为1+(n/2)*(1+1/P)=O(n)。

两者在袜子数量上都是线性的,并且是非常相似的解决方案。让我们稍微修改一下这个问题,承认你在集合中有多双相似的袜子,并且它是轻松存储多双袜子在一个动作(1+ε)。对于K个不同的模式,你可以实现:

  1. 对于每只袜子(n):
    1. 随便拿一只袜子(1)
    2. 把它放在模式的集群上
  2. 对于每个集群(K):
    1. 采取集群和存储袜子对(1+ε)

整体复杂度变成n+K=O(n)。它仍然是线性的,但是选择正确的算法现在可能在很大程度上取决于P和K的值!但是有人可能会再次反对,你可能很难为每只袜子找到(或创建)集群。

此外,您还可以通过查看网站上最好的算法并提出自己的解决方案来节省时间:)

排序的问题0.在你把它们扔进1号洗衣店之前,你把左边的线穿到右边。把它们拿出来,你剪断线,把每一对放在抽屉里——对n对进行2次操作,所以O(n)。

现在下一个问题是,你是否自己洗衣服,你妻子洗她的衣服。这可能是一个0的问题。:)

当我对袜子进行分类时,我做了一个近似的基数排序,将袜子放在相同颜色/图案类型的其他袜子附近。除了在我即将放下袜子的位置/附近看到完全匹配的情况外,我在那个点提取袜子。

几乎所有其他算法(包括USR评分最高的答案)都进行排序,然后删除对。我发现,作为一个人类,最好将一次考虑的袜子数量最小化。

我通过以下方式做到这一点:

  1. 挑选一只与众不同的袜子(不管是什么先引起我的注意)。
  2. 从该概念位置开始基数排序,根据与该位置的相似性从堆中取出袜子。
  3. 把新袜子放在当前的袜子堆附近,距离根据袜子堆的不同而定。如果你发现自己把袜子放在另一只袜子的上面,因为它是相同的,在那里形成一对,然后取下它们。这意味着将来的比较不需要那么费力就能找到正确的位置。

这利用了人类在O(1)时间内进行模糊匹配的能力,这在某种程度上相当于在计算设备上建立哈希映射。

通过首先拉动独特的袜子,您可以留出空间来“放大”不那么独特的特征。

在去掉荧光色、条纹袜子和三双长袜后,你可能最终会得到大部分白色袜子,大致按照它们的磨损程度排序。

在某些时候,袜子之间的差异足够小,其他人不会注意到差异,并且不需要任何进一步的匹配努力。

我刚刚完成了我的袜子配对,我发现最好的方法是这样做:

  • 选择其中一只袜子并将其收起(为该双袜子创建一个“桶”)
  • 如果下一个是前一个的对,则将其放入现有存储桶,否则创建一个新存储桶。

在最坏的情况下,这意味着你将有n/2个不同的桶,你将有n-2个关于哪个桶包含当前袜子对的确定。显然,如果你只有几对,这个算法效果很好;我用12对做了。

它不是很科学,但它工作得很好:)

拿起第一只袜子,放在桌子上。现在再拿一只袜子;如果它和第一只袜子匹配,把它放在第一只袜子的上面。如果不匹配,把它放在离第一只袜子稍远的桌子上。再拿第三只袜子;如果它和前两只袜子匹配,把它放在上面,或者把它放在离第三只袜子稍远的地方。重复,直到你把所有的袜子都捡起来。

我注意到所有的答案都忽略了这样一个事实,即有两点你可以执行预处理,而不会减慢你的整体洗衣性能。

此外,我们不需要假设有大量的袜子,即使是对大家庭来说也是如此。袜子从抽屉里拿出来,穿过,然后被扔在一个地方(也许是垃圾箱),在被清洗之前它们会呆在那里。虽然我不会把这个垃圾箱称为LIFO堆栈,但我想说可以肯定的是

  1. 人们把两只袜子大致扔在同一区域bin,
  2. 垃圾箱在任何时候都不是随机的,因此
  3. 从这个bin顶部获取的任何子集通常都包含一双袜子。

由于我所知道的所有洗衣机的尺寸都是有限的(不管你要洗多少袜子),而且实际的随机化发生在洗衣机中,不管我们有多少袜子,我们总是有一些几乎不包含单例的小子集。

我们的两个预处理阶段是“将袜子放在晾衣绳上”和“从晾衣绳上取出袜子”,这是我们必须做的,以便获得不仅干净而且干燥的袜子。与洗衣机一样,晾衣绳是有限的,我假设我们有将袜子放在视线内的整个生产线。

下面是put_socks_on_line()的算法:

while (socks left in basket) {take_sock();if (cluster of similar socks is present) {Add sock to cluster (if possible, next to the matching pair)} else {Hang it somewhere on the line, this is now a new cluster of similar-looking socks.Leave enough space around this sock to add other socks later on}}

不要浪费时间移动袜子或寻找最佳匹配,这一切都应该在O(n)中完成,我们还需要将它们放在未排序的线上。袜子还没有配对,我们在线上只有几个相似性簇。这里我们有一组有限的袜子是有帮助的,因为这有助于我们创建“好”的集群(例如,如果袜子集中只有黑色袜子,那么按颜色聚类就不是办法)

下面是take_socks_from_line()的算法:

while(socks left on line) {take_next_sock();if (matching pair visible on line or in basket) {Take it as well, pair 'em and put 'em away} else {put the sock in the basket}

我应该指出,为了提高剩余步骤的速度,明智的做法是不要随机挑选下一只袜子,而是从每个集群中依次取出袜子。这两个预处理步骤都不会花费更多的时间,而不仅仅是把袜子放在线上或篮子里,无论如何我们都必须这样做,所以这应该会大大提高洗衣性能。

之后,很容易做哈希划分算法。通常,大约75%的袜子已经配对,给我留下了一个非常小的袜子子集,这个子集已经(有些)聚类了(在预处理步骤之后,我没有在我的篮子中引入太多熵)。另一件事是剩余的集群往往足够小,可以一次处理,所以有可能从篮子中取出整个集群。

下面是sort_remaining_clusters()的算法:

while(clusters present in basket) {Take out the cluster and spread itProcess it immediatelyLeave remaining socks where they are}

这就是我将以前未配对的袜子引入系统并在没有任何特殊算法的情况下处理剩余袜子的地方-剩余的袜子非常少,并且可以在视觉上非常快速地处理。

对于所有剩余的袜子,我假设它们的对应物仍然没有洗,并将它们放在下一次迭代中。如果您随着时间的推移记录到未配对袜子的增长(“袜子泄漏”),您应该检查您的垃圾箱-它可能会被随机分配(您有猫睡在那里吗?)

我知道这些算法需要很多假设:一个作为某种LIFO堆栈的垃圾箱,一个有限的、正常的洗衣机,以及一个有限的、正常的晾衣绳——但这仍然适用于大量的袜子。

关于并行:只要你把两只袜子扔进同一个垃圾箱,你就可以轻松地并行化所有这些步骤。

我提出的解决方案假设所有袜子的细节都相同,除了颜色。如果袜子之间有更多细节需要推迟,这些细节可以用来定义不同类型的袜子,而不是我例子中的颜色。

假设我们有一堆袜子,一只袜子可以有三种颜色:蓝色、红色或绿色。

然后我们可以为每种颜色创建一个并行工作进程;它有自己的列表来填充相应的颜色。

At time i:
Blue  read  Pile[i]    : If Blue  then Blue.Count++  ; B=TRUE  ; sync
Red   read  Pile[i+1]  : If Red   then Red.Count++   ; R=TRUE  ; sync
Green read  Pile [i+2] : If Green then Green.Count++ ; G=TRUE  ; sync

使用同步过程:

Sync i:
i++
If R is TRUE:i++If G is TRUE:i++

这需要初始化:

Init:
If Pile[0] != Blue:If      Pile[0] = Red   : Red.Count++Else if Pile[0] = Green : Green.Count++
If Pile[1] != Red:If Pile[0] = Green : Green.Count++

哪里

Best Case: B, R, G, B, R, G, .., B, R, G
Worst Case: B, B, B, .., B
Time(Worst-Case) = C * n ~ O(n)
Time(Best-Case) = C * (n/k) ~ O(n/k)
n: number of sock pairsk: number of colorsC: sync overhead

如果“移动”操作相当昂贵,而“比较”操作很便宜,并且无论如何都需要将整个集合移动到一个缓冲区中,其中搜索比原始存储快得多……只需将排序集成到强制移动中。

我发现将分类过程整合到悬挂晾干中变得轻而易举。无论如何,我都需要拿起每只袜子,并将其悬挂(移动),并且将其挂在字符串上的特定位置几乎不需要花费任何费用。现在只是为了不强制搜索整个缓冲区(字符串),我选择按颜色/阴影放置袜子。左边更暗,右边更亮,前面更丰富多彩等。现在,在我挂起每只袜子之前,如果已经有匹配的袜子,我会查看它的“右附近”-这将“扫描”限制为2-3只其他袜子-如果是,我会将另一只挂在它旁边。然后我把它们卷成对,同时从绳子上取下,干燥时。

这可能看起来与顶部答案所建议的“按颜色形成堆栈”没有什么不同,但首先,不选择离散的堆栈,而是选择范围,我对“紫色”是“红色”还是“蓝色”堆栈进行分类没有问题;它只是介于两者之间。然后通过整合两个操作(挂起晾干和排序),挂起时排序的开销大约是单独排序的10%。

我的解决方案并不完全符合您的要求,因为它正式需要O(n)“额外”空间。然而,考虑到我的条件,它在我的实际应用中非常有效。因此,我认为它应该很有趣。

与其他任务相结合

我的特殊情况是,我不用烘干机,只是把我的布料挂在普通的烘干机上。挂布料需要O(n)操作(顺便说一句,我在这里总是考虑箱包装问题),这个问题本质上需要线性的“额外”空间。当我从桶里拿一只新袜子时,如果这双袜子已经挂好了,我会试着把它挂在它的旁边。如果是一双新袜子,我会在它旁边留一些空间。

Oracle机器更好;-)

显然需要一些额外的工作来检查是否有匹配的袜子已经挂在某个地方,并且它将呈现计算机的系数约为1/2的解决方案O(n^2)。但在这种情况下,“人为因素”实际上是一个优势-我通常可以很快(几乎O(1))识别匹配的袜子是否已经挂起(可能涉及一些难以察觉的大脑缓存)-认为它是一种有限的“预言”,如oracleMachine;-)我们人类在某些情况下比数字机器具有这些优势;-)

几乎O(n)

因此,将袜子配对问题与挂布问题联系起来,我免费获得O(n)“额外空间”,并且有一个关于O(n)时间的解决方案,只需要比简单的挂布多一点的工作,并且即使在非常糟糕的星期一早上也可以立即访问完整的袜子…;-)

你试图解决错误的问题。

解决方案1:每次你把脏袜子放进洗衣篮时,把它们打成一个小结。这样你就不必在洗涤后进行任何分类。把它想象成在Mongo数据库中注册索引。未来节省一些CPU的工作。

解决方案2:如果是冬天,你不必穿配套的袜子。我们是程序员。没有人需要知道,只要它有效。

解决方案3:分散工作。你想在不阻塞UI的情况下异步执行如此复杂的CPU进程。把那堆袜子塞进一个袋子里。只有在你需要的时候才找一双。这样它所花费的工作量就不那么明显了。

希望这有帮助!

从一堆袜子到袜子配对的高效算法

先决条件

  1. 那堆袜子里至少得有一只
  2. 桌子必须足够大以容纳N/2袜子(最坏的情况),其中N是总数袜子。

算法

尝试:

  1. 挑第一只袜子
  2. 把它放在桌子上
  3. 挑选下一只袜子,看看它(可能会抛出“没有更多的袜子在堆里”例外)
  4. 现在扫描桌子上的袜子(如果桌子上没有袜子,则抛出异常)
  5. 有火柴吗?a)yes=>从表中删除匹配的袜子b)no=>把袜子放在桌子上(可能抛出'桌子不够大'异常)

除了:

  • 表不够大:
    小心地将所有未配对的袜子混合在一起,然后恢复操作
       //此操作将产生一个新桩和一个空表

  • 桌子上没有袜子:
       扔(最后一只不能换的袜子)

  • 没有袜子留在堆里:
       出口洗衣房

最后:

  • 如果一堆里还有袜子:
       goto 3

已知问题

算法将进入一个无限循环,如果周围没有表或桌子上没有足够的地方容纳至少一只袜子。

可能的改进

根据要排序的袜子数量,吞吐量可能是通过整理桌子上的袜子来增加,只要有足够的空间。

为了使其工作,需要一个具有唯一属性的属性每双袜子的值。这样的属性可以很容易地从袜子的视觉属性合成。

按所述属性对表上的袜子进行排序。让我们称该属性为将袜子排成一排,然后将深色的袜子放在右边(即push_back())和左边颜色较浅的袜子(即。.push_front())

对于巨大的桩,特别是以前看不见的袜子,属性合成可能需要大量时间,因此吞吐量显然会降低。但是,这些属性可以保存在内存中并重用。

需要一些研究来评估这种可能的效率问题如下:

  • 使用上述方法配对的袜子的最佳数量是多少改善?
  • 对于给定数量的袜子,之前需要多少次迭代吞吐量增加?
    a)最后一次迭代
    b)对于所有迭代

PoC符合MCVE指南:

#include <iostream>#include <vector>#include <string>#include <time.h>
using namespace std;
struct pileOfsocks {pileOfsocks(int pairCount = 42) :elemCount(pairCount<<1) {srand(time(NULL));socks.resize(elemCount);
vector<int> used_colors;vector<int> used_indices;
auto getOne = [](vector<int>& v, int c) {int r;do {r = rand() % c;} while (find(v.begin(), v.end(), r) != v.end());v.push_back(r);return r;};
for (auto i = 0; i < pairCount; i++) {auto sock_color = getOne(used_colors, INT_MAX);socks[getOne(used_indices, elemCount)] = sock_color;socks[getOne(used_indices, elemCount)] = sock_color;}}
void show(const string& prompt) {cout << prompt << ":" << endl;for (auto i = 0; i < socks.size(); i++){cout << socks[i] << " ";}cout << endl;}
void pair() {for (auto i = 0; i < socks.size(); i++) {std::vector<int>::iterator it = find(unpaired_socks.begin(), unpaired_socks.end(), socks[i]);if (it != unpaired_socks.end()) {unpaired_socks.erase(it);paired_socks.push_back(socks[i]);paired_socks.push_back(socks[i]);}elseunpaired_socks.push_back(socks[i]);}
socks = paired_socks;paired_socks.clear();}
private:int elemCount;vector<int> socks;vector<int> unpaired_socks;vector<int> paired_socks;};
int main() {pileOfsocks socks;
socks.show("unpaired socks");socks.pair();socks.show("paired socks");
system("pause");return 0;}

两种思路,查找任何匹配项所需的速度,以及与存储相比查找所有匹配项所需的速度。

对于第二种情况,我想指出一个GPU并行版本,它可以查询所有匹配的袜子。

如果你有多个属性要匹配,你可以使用分组元组和更高级的zip迭代器以及推力的转换函数,为了简单起见,这里有一个简单的基于GPU的查询:

//test.cu#include <thrust/device_vector.h>#include <thrust/sequence.h>#include <thrust/copy.h>#include <thrust/count.h>#include <thrust/remove.h>#include <thrust/random.h>#include <iostream>#include <iterator>#include <string>
// Define some types for pseudo code readabilitytypedef thrust::device_vector<int> GpuList;typedef GpuList::iterator          GpuListIterator;
template <typename T>struct ColoredSockQuery : public thrust::unary_function<T,bool>{ColoredSockQuery( int colorToSearch ){ SockColor = colorToSearch; }
int SockColor;
__host__ __device__bool operator()(T x){return x == SockColor;}};

struct GenerateRandomSockColor{float lowBounds, highBounds;
__host__ __device__GenerateRandomSockColor(int _a= 0, int _b= 1) : lowBounds(_a), highBounds(_b) {};
__host__ __device__int operator()(const unsigned int n) const{thrust::default_random_engine rng;thrust::uniform_real_distribution<float> dist(lowBounds, highBounds);rng.discard(n);return dist(rng);}};
template <typename GpuListIterator>void PrintSocks(const std::string& name, GpuListIterator first, GpuListIterator last){typedef typename std::iterator_traits<GpuListIterator>::value_type T;
std::cout << name << ": ";thrust::copy(first, last, std::ostream_iterator<T>(std::cout, " "));std::cout << "\n";}
int main(){int numberOfSocks = 10000000;GpuList socks(numberOfSocks);thrust::transform(thrust::make_counting_iterator(0),thrust::make_counting_iterator(numberOfSocks),socks.begin(),GenerateRandomSockColor(0, 200));
clock_t start = clock();
GpuList sortedSocks(socks.size());GpuListIterator lastSortedSock = thrust::copy_if(socks.begin(),socks.end(),sortedSocks.begin(),ColoredSockQuery<int>(2));clock_t stop = clock();
PrintSocks("Sorted Socks: ", sortedSocks.begin(), lastSortedSock);
double elapsed = (double)(stop - start) * 1000.0 / CLOCKS_PER_SEC;std::cout << "Time elapsed in ms: " << elapsed << "\n";
return 0;}
//nvcc -std=c++11 -o test test.cu

1000万袜子的运行时间:9 ms

正如许多作者所指出的那样,基数排序是一种对袜子进行分类的有效方法。尚未提出的是一种完美的散列方法。使用每双袜子的购买时间就是这样的散列。只需在购买袜子时按顺序编号,就可以在您浏览一堆袜子时将它们放在自己的编号抽屉里。

例如最多24双袜子。请注意,更大的袜子隔间消除了将袜子卷在一起的需要,即所谓的速度/存储权衡。

最多24双袜子的示例。请注意,更大的袜子隔间消除了将袜子卷在一起的需要,即所谓的速度/存储权衡.