2012-03-14 24 views
10

ढूंढें मुझे एक अनुक्रमित अनुक्रम में डुप्लीकेट खोजने के लिए एक बहुत ही कुशल तरीका चाहिए। यह क्या मैं के साथ आया है, लेकिन यह कुछ कमियों, अर्थात् यहएक अनुक्रमित अनुक्रम में डुप्लीकेट को कुशलतापूर्वक

  1. अनावश्यक रूप से परे 2
  2. उपज डुप्लिकेट से पहले
  3. बनाता कई मध्यवर्ती दृश्यों पूरा अनुक्रम की खपत घटनाओं में गिना जाता है है

module Seq = 
    let duplicates items = 
    items 
    |> Seq.countBy id 
    |> Seq.filter (snd >> ((<) 1)) 
    |> Seq.map fst 

कमियों के बावजूद, मुझे कोई कारण नहीं दिख रहा है दो बार कोड के साथ इसे बदलने के लिए। तुलनात्मक रूप से संक्षेप कोड के साथ इसे सुधारना संभव है?

+0

संभावित डुप्लिकेट [मैं संदर्भों का उपयोग किये बिना एफ # अनुक्रम में डुप्लीकेट कैसे हटा सकता हूं] (http://stackoverflow.com/questions/6842466/how-can-i-remove-duplicates-in-an-f-sequence -without-use-references) – gradbot

+1

दरअसल, यह उलटा है। मैं केवल डुप्लिकेट चाहता हूं। – Daniel

+0

हम्म, आप उन मूल्यों को कैसे स्टोर करना चाहते हैं जिन्हें आप पहले से देख चुके हैं? सेट? शब्दकोश? – gradbot

उत्तर

7

यहाँ एक अनिवार्य समाधान (जो बेशक थोड़ा लंबा है):

let duplicates items = 
    seq { 
     let d = System.Collections.Generic.Dictionary() 
     for i in items do 
      match d.TryGetValue(i) with 
      | false,_ -> d.[i] <- false   // first observance 
      | true,false -> d.[i] <- true; yield i // second observance 
      | true,true ->()      // already seen at least twice 
    } 
+0

मैंने सोचा कि यह अच्छा है क्योंकि यह अच्छा है, लेकिन लगा कि यह पूछने लायक था। – Daniel

+0

मैंने एक ही कोड लिखा लेकिन आपने मुझे दो मिनट तक हराया। :) – gradbot

1

मान लिया जाये कि अपने अनुक्रम परिमित है, इस समाधान क्रम पर एक रन की आवश्यकता है:

open System.Collections.Generic 
let duplicates items = 
    let dict = Dictionary() 
    items |> Seq.fold (fun acc item -> 
          match dict.TryGetValue item with 
          | true, 2 -> acc 
          | true, 1 -> dict.[item] <- 2; item::acc 
          | _ -> dict.[item] <- 1; acc) [] 
     |> List.rev 

आप Dictionary की क्षमता के रूप में अनुक्रम की लंबाई प्रदान कर सकते हैं, लेकिन यह पूरे अनुक्रम एक बार फिर की गणना करने की आवश्यकता है।

संपादित करें: 2 समस्या को हल करने से एक मांग पर डुप्लिकेट उत्पन्न कर सकता है:

open System.Collections.Generic 
let duplicates items = 
    seq { 
     let dict = Dictionary() 
     for item in items do 
      match dict.TryGetValue item with 
      | true, 2 ->() 
      | true, 1 -> dict.[item] <- 2; yield item 
      | _ -> dict.[item] <- 1 
    } 
+0

ध्यान दें कि यह डैनियल की दूसरी समस्या का समाधान नहीं करता है। – kvb

1

कार्यात्मक समाधान:

let duplicates items = 
    let test (unique, result) v = 
    if not(unique |> Set.contains v) then (unique |> Set.add v ,result) 
    elif not(result |> Set.contains v) then (unique,result |> Set.add v) 
    else (unique, result) 
    items |> Seq.fold test (Set.empty, Set.empty) |> snd |> Set.toSeq 
+0

[1; 1; 1; 2; 3; 4; 4; 5] इसे दो बार प्रिंट करने का कारण बनता है। – gradbot

+0

@gradbot - आप सही हैं, धन्यवाद, मैंने इसे – MiMo

+0

तय किया है, हमारे एल्गोरिदम बहुत अलग हैं, जबकि आपके सेट अलग-अलग हैं, जबकि मेरा विवाद अलग है। मुझे आश्चर्य है, जो तेजी से होगा? – gradbot

2

यह सबसे अच्छा "कार्यात्मक" समाधान है जिसके साथ मैं आ सकता हूं जो पूरे अनुक्रम को सामने नहीं लेता है।

let duplicates = 
    Seq.scan (fun (out, yielded:Set<_>, seen:Set<_>) item -> 
     if yielded.Contains item then 
      (None, yielded, seen) 
     else 
      if seen.Contains item then 
       (Some(item), yielded.Add item, seen.Remove item) 
      else 
       (None, yielded, seen.Add item) 
    ) (None, Set.empty, Set.empty) 
    >> Seq.Choose (fun (x,_,_) -> x) 
+0

क्यों Seq.skip? आप Seq.filter और Seq.map संयोजन को Seq.choose – MiMo

+0

के साथ प्रतिस्थापित कर सकते हैं अच्छी पकड़, मैं चुनने के बारे में भूल गया। छोड़ना पहले कोड का एक आर्टिफैक्ट था। – gradbot

+0

आप देखे जाने से छुटकारा पा सकते हैं। हटाएं - शायद थोड़ी सी गति प्राप्त हो रही है, और फिर आपका समाधान मेरे जैसा होगा - सेट छेड़छाड़ करेंगे - इसके अलावा मेरा समाधान अनुक्रम को आगे बढ़ाएगा, और इसलिए मुझे लगता है कि आपका बेहतर है (इसलिए +1)। चतुरता के लिए – MiMo

10

एक और अधिक सुरुचिपूर्ण कार्यात्मक समाधान:

let duplicates xs = 
    Seq.scan (fun xs x -> Set.add x xs) Set.empty xs 
    |> Seq.zip xs 
    |> Seq.choose (fun (x, xs) -> if Set.contains x xs then Some x else None) 

का उपयोग करता है scan अब तक देखा सभी तत्वों के सेट जमा करने के लिए। इसके बाद तत्वों के सेट के साथ प्रत्येक तत्व को गठबंधन करने के लिए zip का उपयोग करता है। अंत में, choose का उपयोग पहले से देखे गए तत्वों के सेट में मौजूद तत्वों को फ़िल्टर करने के लिए करता है, यानी डुप्लिकेट।

संपादित

असल में मेरी मूल जवाब पूरी तरह से गलत था। सबसे पहले, आप अपने आउटपुट में डुप्लीकेट नहीं चाहते हैं। दूसरा, आप प्रदर्शन चाहते हैं।

यहाँ एक पूरी तरह कार्यात्मक समाधान है कि एल्गोरिथ्म आप के बाद कर रहे हैं लागू करता है:

let duplicates xs = 
    (Map.empty, xs) 
    ||> Seq.scan (fun xs x -> 
     match Map.tryFind x xs with 
     | None -> Map.add x false xs 
     | Some false -> Map.add x true xs 
     | Some true -> xs) 
    |> Seq.zip xs 
    |> Seq.choose (fun (x, xs) -> 
     match Map.tryFind x xs with 
     | Some false -> Some x 
     | None | Some true -> None) 

यह ट्रैक करने के लिए है कि क्या प्रत्येक तत्व से पहले एक या कई बार देखा गया है किसी मैप का उपयोग करता है और उसके बाद ही वह तत्व का उत्सर्जन करता है देखा जाता है कि केवल एक बार पहले देखा जा रहा था, यानी पहली बार इसे डुप्लिकेट किया गया है।

let duplicates (xs: _ seq) = 
    seq { let d = System.Collections.Generic.Dictionary(HashIdentity.Structural) 
     let e = xs.GetEnumerator() 
     while e.MoveNext() do 
      let x = e.Current 
      let mutable seen = false 
      if d.TryGetValue(x, &seen) then 
      if not seen then 
       d.[x] <- true 
       yield x 
      else 
      d.[x] <- false } 

यह आपके अन्य उत्तर में से किसी की तुलना में तेजी लगभग 2 × (लेखन के समय) है:

यहाँ एक तेजी से जरूरी संस्करण है।

एक क्रम में तत्वों की गणना करने में एक for x in xs do पाश का उपयोग करना GetEnumerator सीधे का उपयोग कर, लेकिन पैदा अपनी खुद की Enumerator काफी yield के साथ एक गणना अभिव्यक्ति का उपयोग कर की तुलना में तेजी नहीं है की तुलना में काफी धीमी है।

ध्यान दें कि Dictionary की TryGetValue सदस्य मुझे एक ढेर आवंटित मूल्य परिवर्तनशील जबकि TryGetValue विस्तार सदस्य (उसकी/उसके जवाब में KVB द्वारा और प्रयोग किया जाता) एफ # द्वारा की पेशकश की अपनी वापसी टपल आवंटित द्वारा भीतरी पाश में आवंटन से बचने के लिए अनुमति देता है।

+1

+1, लेकिन यह मेरे मूल समाधान से काफी खराब है। – Daniel

+0

@ डैनियल ओप्स, मैं भूल गया कि यह कुशल होना चाहिए! :-) –

+2

अनिवार्य संस्करण में बहुत अच्छे सूक्ष्म सुधार। संयोग से, मुझे यकीन है कि कीथ (केवीबी) एक "वह" है। :-) – Daniel

संबंधित मुद्दे