2015-07-13 27 views
1

我正在瀏覽我的系統字典並根據嚴格定義查找任何其他單詞的子集或超集。在枚舉數組時通過動態刪除數組中的元素

下面的實現不起作用,但如果它實現了,我認爲這將是非常有效的。我如何遍歷數組,並在迭代過程中從同一個數組中刪除項目?

def collect_dead_words 
    result = @file #the words in my system dictionary, as an array 
    wg = WordGame.new # the class that "knows" the find_subset_words & 
        # find_superset_words methods 
    result.each do |value| 
    wg.word = value 
    supersets = wg.find_superset_words.values.flatten 
    subsets = wg.find_subset_words.values.flatten 
    result.delete(value) unless (matches.empty? && subsets.empty?) 
    result.reject! { |cand| supersets.include? cand } 
    result.reject! { |cand| subsets.include? cand } 
    end 
    result 
end 

注:find_superset_wordsfind_subset_words都返回哈希值,因此values.flatten

+0

您可能希望編輯以澄清'subset'和'superset'的定義。 [可能的興趣](http://www.independent.co.uk/arts-entertainment/classical/features/the-enduring-myth-of-music-and-maths-2307387.html)。 –

+0

我確實沒有留下所有這些東西,因爲嚴格來說,這並不在問題的範圍之內,即我在遍歷它時如何展開集合。我想現在我們正在進行不同的對話。 – pgblu

回答

1

這就是我想出了根據您的反饋(加用最短的話開始進一步優化)受歡迎的!

+0

感謝您的編輯建議。在您離開擬議的編輯之前或之後,我注意並編輯了未完成的句子。旁白:去熊! ('65)。大多數作曲家都擅長數學嗎?你現在把自己當作Ruby作曲家嗎? –

+0

感謝您的回答,這對我的算法設計幫助很大。我不知道具有數學天賦的作曲家,是的,在過去的幾年裏,我對'作曲家'的定義不得不大大擴展。很高興認識你,@CarySwoveland! – pgblu

5

這是不可取的,而遍歷它來修改的集合。相反,要麼迭代集合的副本,要麼創建一個單獨的數組以便稍後移除。

+0

除了每個單詞生成一組不需要再次檢查的子集和超集之外,我想通過從集合中刪除它們的內容來最小化不必要的週期。 – pgblu

+1

我會使用第二種方法(使用'Set'而不是'Array'),然後測試集合中的成員資格以查看是否應跳過當前單詞。同樣,你可以使用'delete_if'作爲@seph演示來刪除當前值,但除了迭代期間沒有迭代器支持刪除之外;你最終會跳過你不想跳過的東西。 – Amadan

1

完成此操作的一種方法是使用Array#delete_if。這是我在運行它,所以你得到的想法:

def collect_dead_words 
    result = [] 
    collection = @file 
    num = @file.max_by(&:length).length 
    1.upto(num) do |index| 
    subset_by_length = collection.select {|word| word.length == index } 
    while !subset_by_length.empty? do 
     wg = WordGame.new(subset_by_length[0]) 
     supermatches = wg.find_superset_words.values.flatten 
     submatches = wg.find_subset_words.values.flatten 
     collection.reject! { |cand| supermatches.include? cand } 
     collection.reject! { |cand| submatches.include? cand } 
     result << wg.word if (supermatches.empty? && submatches.empty?) 
     subset.delete(subset_by_length[0]) 
     collection.delete(subset_by_length[0]) 
    end 
    end 
    result 
end 

進一步優化:

supersets_and_subsets = [] 

result.delete_if do |el| 
    wg.word = el 
    superset_and_subset = wg.find_superset_words.values.flatten + wg.find_subset_words.values.flatten 
    supersets_and_subsets << superset_and_subset 

    !superset_and_subset.empty? 
end 

result -= supersets_and_subsets.flatten.uniq 
+0

'supersets_and_subsets'的減法發生得太晚了,也就是說,在'find'方法碰到這些集合之前它就需要發生。如果「超級計算機」是「萌芽」的超級詞,那麼檢查「萌芽」意味着我不必檢查「超級計算機」。 – pgblu

1

問題

據我所知,串s1是串s2的一個子集,如果之後的零個或多個字符從s2除去s1 == s2;即,如果存在映射的索引的ms1使得:

  • 每個索引is1s1[i] = s2[m(i)];和
  • if i < j then m(i) < m(j)

此外s2s1的超集當且僅當是s1s2一個子集。

請注意,對於s1成爲s2的子集,s1.size <= s2.size必須爲真。

例如:

  • 「貓」是「工藝」,因爲如果「r」和「F」被除去後者成爲「貓」的子集。
  • 「貓」不是「可愛」的子集,因爲「可愛」沒有「a」。
  • 「貓」不是「at」的超集,因爲「cat」.include?(「at」)#=> true`。
  • 「貓」不是「制定」的子集,因爲m(0) = 3m(1) = 2,但m(0) < m(1)是錯誤的;

算法

子集(並因此超集)是一個傳遞關係,其允許算法顯著效率。我的意思是,如果s1s2的子集並且s2s3的子集,則s1s3的子集。

我將如下進行:

  • 創建空集neither_sub_nor_suplongest_sups和空數組subs_and_sups
  • 按字長排序單詞,最長的第一個。
  • w加上neither_sub_nor_sup,其中w是字典中最長的單詞。
  • 對於字典中的每個字隨後w(最長至最短),執行以下操作:
    • 爲每個元素的neither_sub_nor_supu確定是否wu一個子集。如果是,請將uneither_sub_nor_sup移動到longest_sups,並將u附加到subs_and_sups
    • 如果一個或多個元素從neither_sub_nor_sup移動到longest_sups,則附加wsubs_and_sups;否則將w加到neither_sub_nor_sup
  • 返回subs_and_sups

代碼

require 'set' 

def identify_subs_and_sups(dict) 
    neither_sub_nor_sup, longest_sups = Set.new, Set.new 
    dict.sort_by(&:size).reverse.each_with_object([]) do |w,subs_and_sups| 
    switchers = neither_sub_nor_sup.each_with_object([]) { |u,arr| 
     arr << u if w.subset(u) } 
    if switchers.any? 
     subs_and_sups << w 
     switchers.each do |u| 
     neither_sub_nor_sup.delete(u) 
     longest_sups << u 
     subs_and_sups << u 
     end 
    else 
     neither_sub_nor_sup << w 
    end 
    end 
end 

class String 
    def subset(w) 
    w =~ Regexp.new(self.gsub(/./) { |m| "#{m}\\w*" }) 
    end 
end 

dict = %w| cat catch craft cutie enact trivial rivert river | 
    #=> ["cat", "catch", "craft", "cutie", "enact", "trivial", "rivert", "river"] 
identify_subs_and_sups(dict) 
    #=> ["river", "rivert", "cat", "catch", "craft"] 

不像從最長的詞典處理的話至最短,我們可以代替他們最短責令最長:

def identify_subs_and_sups1(dict) 
    neither_sub_nor_sup, shortest_sups = Set.new, Set.new 
    dict.sort_by(&:size).each_with_object([]) do |w,subs_and_sups| 
    switchers = neither_sub_nor_sup.each_with_object([]) { |u,arr| 
     arr << u if u.subset(w) } 
    if switchers.any? 
     subs_and_sups << w 
     switchers.each do |u| 
     neither_sub_nor_sup.delete(u) 
     shortest_sups << u 
     subs_and_sups << u 
     end 
    else 
     neither_sub_nor_sup << w 
    end 
    end 
end 

identify_subs_and_sups1(dict) 
    #=> ["craft", "cat", "rivert", "river"] 

基準

(未完待續......)

1 OP指出(在後面的評論),其s1是不是s2的子字符串,如果s2.include?(s1) #=> true。我會假裝我從來沒有看到過,因爲它將扳手投入作品。不幸的是,subset不再是與附加要求的傳遞關係。我沒有調查這個問題的含義,但我懷疑這意味着需要一個相當粗暴的算法,可能需要對詞典中所有單詞進行兩兩比較。

+0

我從最短的單詞開始,消除超集,而不是以其他方式發現超集,這是一種更便宜的例程。當我重構代碼時,我一定會考慮在散列上構建例程,並創建.subset?和.superset?方法。 – pgblu

+0

如果您編輯添加字典,我將運行基準。 –

+0

下面是我(可能錯誤地)認爲是不相關的細節,即'子集'的確切定義:它基本上是你的建議,但不包括嚴格的sub * string * s,即'cat'是'craft'的一個子集。但不是'catalog' - 而'catalog' *是'clog'的超集。用正則表達式,「我是什麼的子串?」比「我是一個什麼樣的超弦?」要快得多。然而,它可能是非常不同的「我是什麼*的子字符串/超弦?」? 如果你在Mac上,你可以運行它反對'/ usr/share/dict/words'。 – pgblu