簡體   English   中英

以最小差異大於 Python 列表中的值對大多數數字進行采樣的最快方法

[英]Fastest way to sample most numbers with minimum difference larger than a value from a Python list

給定一個包含 20 個浮點數的列表,我想找到一個最大的子集,其中任意兩個候選者彼此之間的差異大於mindiff = 1. . 現在我正在使用一種蠻力方法使用itertools.combinations從最大到最小的子集進行搜索。 如下圖,代碼在 4 s 后為 20 個數字的列表找到了一個子集。

from itertools import combinations
import random
from time import time

mindiff = 1.
length = 20
random.seed(99)
lst = [random.uniform(1., 10.) for _ in range(length)]

t0 = time()
n = len(lst)
sample = []
found = False
while not found:
    # get all subsets with size n
    subsets = list(combinations(lst, n))
    # shuffle to ensure randomness
    random.shuffle(subsets)
    for subset in subsets:
        # sort the subset numbers
        ss = sorted(subset)
        # calculate the differences between every two adjacent numbers
        diffs = [j-i for i, j in zip(ss[:-1], ss[1:])]
        if min(diffs) > mindiff:
            sample = set(subset)
            found = True
            break
    # check subsets with size -1
    n -= 1

print(sample)
print(time()-t0)

輸出:

{2.3704888087015568, 4.365818049020534, 5.403474619948962, 6.518944556233767, 7.8388969285727015, 9.117993839791751}
4.182451486587524

但是,實際上我有一個包含 200 個數字的列表,這對於粗暴枚舉是不可行的。 我想要一種快速算法來僅對一個最小差異大於 1 的隨機最大子集進行采樣。請注意,我希望每個樣本都具有隨機性和最大大小。 有什么建議?

我之前的回答假設您只想要一個最佳解決方案,而不是所有解決方案的統一隨機樣本。 這個答案假設您想要一個從所有這些最佳解決方案中統一采樣的答案。

  1. 構造一個有向無環圖G ,其中每個點有一個節點,當b - a > mindist時,節點ab相連。 還要添加兩個虛擬節點st ,其中s -> x代表所有xx -> t代表所有x

  2. 對於G每個節點,計算到t存在多少條長度為k路徑。 您可以使用帶有表P[x][k]動態編程在O(n^2 k)時間內有效地完成此操作,最初填充P[x][0] = 0除了P[t][0] = 1 ,然后P[x][k] = sum(P[y][k-1] for y in neighbors(x))

    繼續這樣做,直到達到最大k - 您現在知道最佳子集的大小。

  3. 使用P對從st的長度為k的路徑進行均勻采樣以加權您的選擇。

    這是通過從s開始完成s 然后我們查看s每個鄰居並隨機選擇一個,其權重由P[s][k] 這給了我們最優集合的第一個元素。

    然后我們重復執行這一步。 我們正處在x ,看的鄰居x和選擇一個隨機使用權P[x][ki]這里i是步驟我們在。

  4. 使用您在 3 中采樣的節點作為您的隨機子集。

在純 Python 中實現上述內容:

import random

def sample_mindist_subset(xs, mindist):
    # Construct directed graph G.
    n = len(xs)
    s = n; t = n + 1  # Two virtual nodes, source and sink.
    neighbors = {
        i: [t] + [j for j in range(n) if xs[j] - xs[i] > mindist]
        for i in range(n)}
    neighbors[s] = [t] + list(range(n))
    neighbors[t] = []

    # Compute number of paths P[x][k] from x to t of length k.
    P = [[0 for _ in range(n+2)] for _ in range(n+2)]
    P[t][0] = 1
    for k in range(1, n+2):
        for x in range(n+2):
            P[x][k] = sum(P[y][k-1] for y in neighbors[x])

    # Sample maximum length path uniformly at random.
    maxk = max(k for k in range(n+2) if P[s][k] > 0)
    path = [s]
    while path[-1] != t:
        candidates = neighbors[path[-1]]
        weights = [P[cn][maxk-len(path)] for cn in candidates]
        path.append(random.choices(candidates, weights)[0])

    return [xs[i] for i in path[1:-1]]

請注意,如果您想從同一組數字中多次采樣,則不必每次都重新計算P並且可以重復使用它。

我可能不完全理解這個問題,因為現在解決方案非常簡單。 編輯:是的,畢竟我誤解了,OP 不僅想要一個最佳解決方案,而且希望從一組最佳解決方案中隨機抽樣 這個答案並沒有錯,但它也是對與 OP 感興趣的問題不同的問題的答案。


簡單地對數字進行排序並貪婪地構造子集:

def mindist_subset(xs, mindist):
    result = []
    for x in sorted(xs):
        if not result or x - result[-1] > mindist:
            result.append(x)
    return result

正確性證明草圖。

假設我們有一個給定輸入數組A的最優解S 如果它不包含min(A)需要注意的是,我們可以刪除min(S)S ,並添加min(A)因為這只會增加之間的距離min(S)和第二小的號碼S 結論:我們可以不失一般性地假設min(A)是最優解的一部分。

現在我們可以遞歸地應用這個論點。 我們將min(A)添加到一個解決方案中,並刪除所有離min(A)太近的元素,給出剩余的元素A' 然后我們剩下一個子問題,其中應用完全相同的參數,我們可以選擇min(A')作為解決方案的下一個元素,等等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM