在Clojure中对数据的大矢量进行排序的最快方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Clojure中对数据的大矢量进行排序的最快方法相关的知识,希望对你有一定的参考价值。

我需要能够以这样一种方式存储集合中的数据,即任何给定密钥集的平均值不大于特定数字。

例如,假设任何给定的3个集合的“:num”的平均值不能大于或等于10,并且我有以下集合:

(def my-set
    ({:num 0}
     {:num 0}
     {:num 0}
     {:num 5}
     {:num 5}
     {:num 5}
     {:num 10}
     {:num 10}
     {:num 10}
))

在这个集合中,最后3个哈希值不能保持彼此相邻,因为它们的平均值为10,但是可以像这样重新组织集合,因为任何给定的3的平均值将是5:

(def my-set
        ({:num 0}
         {:num 5}
         {:num 10}
         {:num 0}
         {:num 5}
         {:num 10}
         {:num 0}
         {:num 5}
         {:num 10}
    ))

我的问题是,假设这个集合远远大于9个条目的样本,可能是数百万个,并且整个集合的平均值是5或更少,那么对这些集合进行排序的最快和最有效的方法是什么任何3(或10或100,或任何给定样本大小)的平均值从未达到10?

这是我的伪代码解决方案,我觉得它太慢了:

1)按值排序集合:数字2)将集合除以2并分割为该索引的数量3)交错第一集合与第二集合的逆序。

所以第一个集合看起来像:

({:num 0} {:num 0} {:num 0} {:num 5})

第二次收集(逆转):

({:num 10} {:num 10} {:num 10} {:num 5} {:num 5))

使用interleave,产生的集合将是:

({0} {当然,无论是{10},是否{0},是否{10},是否{0},{10}当然,无论是5} {5} {当然,无论是5})

必须有一个更好的方法来做到这一点。

答案

这是一种方法:

(def data-9
  [{:num 0}
   {:num 0}
   {:num 0}
   {:num 5}
   {:num 5}
   {:num 5}
   {:num 10}
   {:num 10}
   {:num 10}] )

(def data-11
  (into (vec data-9)
    [{:num 0}
     {:num 10}] ))

(defn interleave-by
  [data group-size]
  (let [data-num       (count data)
        part-size      (quot data-num group-size)
        leftover-num   (- data-num (* part-size group-size))
        data-sorted    (sort-by :num data)
        data-leftover  (take leftover-num data-sorted)
        data-use       (drop leftover-num data-sorted)
        data-parts     (partition-all part-size data-use)
        data-reordered (apply mapcat vector data-parts)
        data-final     (reduce into (vec data-leftover) data-reordered)
        ]
    data-final ))

(newline)
(println (interleave-by data-9 3))

data-num => 
9
part-size => 
3
leftover-num => 
0
data-sorted => 
({:num 0}
 {:num 0}
 {:num 0}
 {:num 5}
 {:num 5}
 {:num 5}
 {:num 10}
 {:num 10}
 {:num 10})
data-leftover => 
()
data-use => 
({:num 0}
 {:num 0}
 {:num 0}
 {:num 5}
 {:num 5}
 {:num 5}
 {:num 10}
 {:num 10}
 {:num 10})
data-parts => 
(({:num 0} {:num 0} {:num 0})
 ({:num 5} {:num 5} {:num 5})
 ({:num 10} {:num 10} {:num 10}))
data-reordered => 
({:num 0}
 {:num 5}
 {:num 10}
 {:num 0}
 {:num 5}
 {:num 10}
 {:num 0}
 {:num 5}
 {:num 10})
data-final => 
[[:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]]
(interleave-by data-9 3) => 
[[:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]]

如果没有group-size的整数倍,则更难。这导致涉及data-leftover的额外复杂性

(newline)
(println (interleave-by data-11 3))


data-num => 
11
part-size => 
3
leftover-num => 
2
data-sorted => 
({:num 0}
 {:num 0}
 {:num 0}
 {:num 0}
 {:num 5}
 {:num 5}
 {:num 5}
 {:num 10}
 {:num 10}
 {:num 10}
 {:num 10})
data-leftover => 
({:num 0} {:num 0})
data-use => 
({:num 0}
 {:num 0}
 {:num 5}
 {:num 5}
 {:num 5}
 {:num 10}
 {:num 10}
 {:num 10}
 {:num 10})
data-parts => 
(({:num 0} {:num 0} {:num 5})
 ({:num 5} {:num 5} {:num 10})
 ({:num 10} {:num 10} {:num 10}))
data-reordered => 
({:num 0}
 {:num 5}
 {:num 10}
 {:num 0}
 {:num 5}
 {:num 10}
 {:num 5}
 {:num 10}
 {:num 10})
data-final => 
[{:num 0}
 {:num 0}
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 5]
 [:num 10]
 [:num 10]]
(interleave-by data-11 3) => 
[{:num 0}
 {:num 0}
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 0]
 [:num 5]
 [:num 10]
 [:num 5]
 [:num 10]
 [:num 10]]
另一答案
  • 这是关于算法的,并且只是偶然地与Clojure有关。
  • 所需要的不是这样排序。我们正在寻找数据的排列,使得没有相邻的三个(或多个)数字平均为五个或更多。在导出排列之前,我们可以映射到数字。
  • 不需要解决方案。例如,如果任何数字超过十五,则没有一个。
  • 这看起来类似于subset sum problem。很可能一个确切的解决方案是难以处理的,但贪婪或其他方法将是切实可行的。

我建议你试试Computer Science site上的问题。

以上是关于在Clojure中对数据的大矢量进行排序的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

Python中最快的排序方式

如何在 Clojure 中对函数进行基准测试?

用非常数除数进行矢量化整数除法的最快方法

在 C++ 中对大量元素进行分组的最快方法

Clojure / Clojurescript:按多个值的地图分组

在 pandas 中对函数进行矢量化