昨天,我在洗衣服时把袜子配对,结果发现我这样做效率不高。我在做一个天真的搜索——挑选一只袜子,然后“反复”寻找那一双袜子。这需要平均在n/2*n/4=n2/8袜子上迭代。

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

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

因此,问题基本上是:

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

我将感谢回答以下方面的问题:

大量袜子的一般理论解。袜子的实际数量没有那么多,我不相信我的配偶和我有超过30双。(而且很容易区分我的袜子和她的袜子;这也可以用吗?)它是否等同于元素清晰度问题?


当前回答

创建一个哈希表,该表将用于不匹配的袜子,使用模式作为哈希。一只一只地重复袜子。如果袜子在哈希表中有图案匹配,请将袜子从表中取出并配对。如果袜子没有火柴,就把它放到桌子上。

其他回答

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

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

在最坏的情况下,这意味着您将有n/2个不同的存储桶,并且您将有n-2个关于哪个存储桶包含当前袜子的确定。显然,如果你只有几对,这个算法会很好地工作;我用了12双鞋。

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

作为实际解决方案:

快速制作一堆易于区分的袜子。(用颜色表示)快速整理每一堆,并使用袜子的长度进行比较。作为一个人,你可以很快地决定用哪只袜子进行分区,以避免最坏的情况。(你可以看到多只袜子平行排列,这对你有利!)当垃圾堆达到一个阈值时,停止分类,在该阈值下,您可以立即找到不合适的袜子和短袜

如果你有1000只袜子,有8种颜色,平均分布,你可以在c*n时间内每125只袜子做4堆。以5只袜子为阈值,你可以在6次跑步中对每一堆袜子进行分类。(数2秒把袜子扔到正确的堆上,只需要不到4小时。)

如果你只有60只袜子、3种颜色和2种袜子(你/你妻子的),你可以在1次跑步中对每一堆10只袜子进行分类(同样阈值=5)。(数2秒,需要2分钟)。

最初的桶排序将加快您的进程,因为它在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个以上的元素,且大小大致相等,那么与排序相比,排序的速度都会有所提高(前提是我们可以直接将袜子分配给它的堆),并且排序可以在较小的数据集上快速进行。

我希望我能为这个问题贡献一些新的东西。我注意到,所有的答案都忽略了这样一个事实,即在不降低整体洗衣性能的情况下,有两点可以执行预处理。

此外,即使是大家庭,我们也不需要假设有大量袜子。袜子从抽屉中取出并穿上,然后在洗衣服之前,将它们扔到一个地方(可能是一个垃圾箱)。虽然我不会将所说的垃圾箱称为后进先出堆栈,但我认为可以安全地假设

人们把两只袜子大致扔在箱子箱子在任何时候都不会随机化,因此从该容器顶部获取的任何子集通常都包含一双袜子。

由于我所知道的所有洗衣机的尺寸都是有限的(不管你要洗多少袜子),而且洗衣机中会发生实际的随机性,所以无论我们有多少袜子,我们总是有几乎不含单品的小子集。

我们的两个预处理阶段是“把袜子放在晾衣绳上”和“把袜子从晾衣绳里拿出来”,我们必须这样做,这样才能得到既干净又干燥的袜子。和洗衣机一样,晾衣绳是有限的,我假设我们可以看到袜子的整个部分。

以下是put_socks_on_ine()的算法:

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_maining_clusters()的算法:

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

之后,只剩下几只袜子了。在这里,我将之前未配对的袜子引入到系统中,并在不使用任何特殊算法的情况下处理剩余的袜子——剩余的袜子非常少,可以非常快速地进行视觉处理。

对于所有剩余的袜子,我假设它们的同伴仍然没有洗,并将它们放在一边,以备下次迭代。如果你记录了一段时间内未配对袜子的增长(“袜子泄漏”),你应该检查你的垃圾箱——它可能会随机出现(你有猫睡在里面吗?)

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

关于并行性:只要你把两个袜子放在同一个箱子里,你就可以很容易地并行化所有这些步骤。

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

假设看袜子和记住它们独特图案的成本可以忽略不计(ε)。那么最好的解决办法就是把所有的袜子都扔到桌子上。这包括以下步骤:

将所有袜子放在一张桌子上(1),并创建一个hashmap{pattern:position}(ε)当有剩余袜子时(n/2):随机挑选一只袜子(1)查找相应袜子的位置(ε)取回袜子(1)并存放

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

把所有袜子扔在桌子上(1)当有剩余袜子时(n/2):随机挑选一只袜子(1)当未配对时(1/P):找到具有相似图案的袜子拿袜子,比较两者(1)如果可以,存储配对

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

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

对于每只袜子(n):随机挑选一只袜子(1)将其放到其模式的集群中对于每个集群(K):取簇并储存袜子(1+ε)

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

此外,你也可以在网站上查找最佳算法,并提出自己的解决方案,从而节省时间:)

Defant&Kravitz(1)给出了一种算法,通过将袜子依次放在脚上和脚下来对袜子进行排序。

他们的算法适用于任意数量的英尺。

本文给出了(定理1.1)可使用单脚排序的袜子订单的特征。从他们的定理1.3可以看出,每一个4种颜色的袜子订单最多可以用两只脚进行排序,而有5种颜色的袜订单不可能用两只脚排序。

Colin Defant和Noah Kravitz,袜子足部分类(2022)