पूर्णांक अनुक्रमों की खोज करना


14

मेरे पास एक काफी जटिल खोज समस्या है जिसे मैंने निम्नलिखित विवरण को कम करने में कामयाब किया है। मैं गुगली कर रहा हूं, लेकिन एक एल्गोरिथ्म को खोजने में सक्षम नहीं हुआ है जो मेरी समस्या को साफ-साफ फिट करता है। विशेष रूप से मनमाने ढंग से पूर्णांकों को छोड़ने की आवश्यकता है। शायद यहाँ कोई मुझे कुछ करने के लिए इशारा कर सकता है?

उदाहरण के लिए पूर्णांक A का क्रम लें, (1 2 3 4)

पूर्णांकों के विभिन्न क्रमों को लें और यदि उनमें से कोई भी ऐसा मिलान करता है तो परीक्षण करें।

  1. A में पूर्णांक के सभी पूर्णांक अनुक्रम में हैं
  2. परीक्षण किए गए अनुक्रम में पूर्णांक के क्रम ए में समान हैं
  3. हम ए में किसी भी पूर्णांक के बारे में परवाह नहीं करते हैं जो परीक्षण अनुक्रम में नहीं हैं
  4. हम केवल पहले ही नहीं, सभी मिलान परीक्षण क्रम चाहते हैं।

एक उदाहरण

A = (1 2 3 4)
B = (1 3)
C = (1 3 4)
D = (3 1)
E = (1 2 5)

बी मैच ए

C, A से मेल खाता है

D A से मेल नहीं खाता क्योंकि ऑर्डर अलग है

E, A से मेल नहीं खाता है क्योंकि इसमें एक पूर्णांक नहीं A है

मुझे उम्मीद है कि यह स्पष्टीकरण पर्याप्त स्पष्ट है। सबसे अच्छा मैंने जो किया है वह परीक्षण अनुक्रमों के एक पेड़ का संरक्षण करने के लिए है और ए पर पुनरावृति है। पूर्णांक को छोड़ने में सक्षम होने के लिए बहुत सारे असफल खोज पथ होते हैं।

धन्यवाद

कुछ सुझावों को पढ़कर मुझे लगता है कि मुझे कुछ ऐसे बिंदुओं को स्पष्ट करना होगा जिन्हें मैंने बहुत अस्पष्ट बताया।

  1. दोहराया संख्याओं की अनुमति है, वास्तव में यह काफी महत्वपूर्ण है क्योंकि यह एकल परीक्षण अनुक्रम को ए से मेल खाने के कई तरीकों की अनुमति देता है

    A = (1234356), B = (236), मैच या तो हो सकते हैं -23 --- 6 या -2--3-6

  2. मुझे उम्मीद है कि बहुत बड़ी संख्या में परीक्षण क्रम होंगे, हजारों में कम से कम और अनुक्रम ए में अधिकतम 20 की लंबाई होगी। इस प्रकार बस एक-एक करके प्रत्येक परीक्षण अनुक्रम को मैच करने की कोशिश कर रहा है, यह अत्यंत अक्षम हो जाता है।

क्षमा करें यदि यह स्पष्ट नहीं था।


4
आप ध्वनि करते हैं जैसे कि आप बाद में पता लगाना चाहते हैं ( en.wikipedia.org/wiki/Subfterence )। क्या यही है? फिर "बाद के एल्गोरिथ्म" के लिए खोज करने का प्रयास करें।
किलिआन फोथ

ईमानदारी से, अधिकतम लंबाई के साथ कुछ हजार अनुक्रम <= 20 मेरे लिए एक बड़ी संख्या नहीं है। एक सरल जानवर बल दृष्टिकोण चाल करना चाहिए। या क्या आपके पास हजारों अनुक्रम "ए" हैं, प्रत्येक को हजारों संभावित बाद के परीक्षणों के खिलाफ परीक्षण करना है?
डॉक्टर ब्राउन

अनुक्रम ए की एक सतत धारा है लेकिन वे एक दूसरे से पूरी तरह से स्वतंत्र हैं। हालांकि एक प्रसंस्करण में देरी सीधे अन्य सभी को विलंबित करेगी, इसलिए गति महत्वपूर्ण है।
डेविड गिब्सन

1
आपकी वर्णमाला कितनी बड़ी है? क्या आपके पास वास्तव में मनमाने ढंग से पूर्णांक हैं, या मानों की एक सीमित श्रेणी है जैसे कि हम कुछ पूर्वाभ्यास कर सकते हैं?
फ्रैंक

पूर्णांक की संभावित सीमा 100,000 में है
डेविड गिब्सन

जवाबों:


18

हम्म, मैं दो संभावित एल्गोरिदम के बारे में सोच सकता हूं: अनुक्रम के माध्यम से एक रैखिक स्कैन , या सूचकांकों के निरंतर-समय की खोज के साथ एक शब्दकोश का निर्माण।

यदि आप एक बड़े अनुक्रम A के विरुद्ध कई संभावित अनुवर्ती B का परीक्षण कर रहे हैं , तो मेरा सुझाव है कि आप शब्दकोश के साथ संस्करण का उपयोग करें।

रैखिक स्कैन

विवरण

हम अनुक्रम ए के लिए एक कर्सर बनाए रखते हैं । फिर हम बाद के बी में सभी वस्तुओं के माध्यम से पुनरावृति करते हैं । प्रत्येक आइटम के लिए, हम को कर्सर को तब तक आगे बढ़ाते हैं जब तक हमें एक मिलान आइटम नहीं मिल जाता है। यदि कोई मिलान आइटम नहीं मिला, तो B एक बाद का नहीं है।

यह हमेशा O (seq.size) में चलता है ।

स्यूडोकोड

शाही शैली:

def subsequence? seq, subseq:
  i = 0
  for item in subseq:
    i++ while i < seq.size and item != seq[i]
    return false if i == seq.size
  return true

कार्यात्मक शैली:

let rec subsequence? = function
| _ [] -> true
| [] _ -> false
| cursor::seq item::subseq ->
  if   cursor = item
  then subsequence? seq subseq
  else subsequence? seq item::subseq

उदाहरण कार्यान्वयन (पर्ल):

use strict; use warnings; use signatures; use Test::More;

sub is_subsequence_i ($seq, $subseq) {
  my $i = 0;
  for my $item (@$subseq) {
    $i++ while $i < @$seq and $item != $seq->[$i];
    return 0 if $i == @$seq;
  }
  return 1;
}

sub is_subsequence_f ($seq, $subseq) {
  return 1 if @$subseq == 0;
  return 0 if @$seq == 0;
  my ($cursor, @seq) = @$seq;
  my ($item, @subseq) = @$subseq;
  return is_subsequence_f(\@seq, $cursor == $item ? \@subseq : $subseq);
}

my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];

for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
  ok $is_subsequence->($A, $B), 'B in A';
  ok $is_subsequence->($A, $C), 'C in A';
  ok ! $is_subsequence->($A, $D), 'D not in A';
  ok ! $is_subsequence->($A, $E), 'E not in A';
  ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}

done_testing;

शब्दकोश खोज

विवरण

हम दृश्य के आइटम नक्शा एक उनके सूचकांकों करने के लिए। फिर हम बी में प्रत्येक आइटम के लिए उपयुक्त सूचक देखते हैं , उन सूचक को छोड़ दें जो छोटे हैं, और छोटी सीमा को कम सीमा के रूप में चुन सकते हैं। जब कोई संकेत नहीं मिलता है, तो बी एक बाद नहीं है।

O (subseq.size · k) जैसी किसी चीज़ में चलता है , जहाँ k बताता है कि वहाँ कितने डुप्लिकेट नंबर हैं seq। साथ ही एक O (seq.size) ओवरहेड

इस समाधान का लाभ यह है कि एक नकारात्मक निर्णय बहुत तेजी से (निरंतर समय तक) तक पहुंचा जा सकता है, एक बार जब आप लुकअप तालिका बनाने के ओवरहेड का भुगतान कर चुके होते हैं।

स्यूडोकोड:

शाही शैली:

# preparing the lookup table
dict = {}
for i, x in seq:
  if exists dict[x]:
    dict[x].append(i)
  else:
    dict[x] = [i]

def subsequence? subseq:
  min_index = -1
  for x in subseq:
    if indices = dict[x]:
      suitable_indices = indices.filter(_ > min_index)
      return false if suitable_indices.empty?
      min_index = suitable_indices[0]
    else:
      return false
  return true

कार्यात्मक शैली:

let subsequence? subseq =
  let rec subseq-loop = function
  | [] _ -> true
  | x::subseq min-index ->
    match (map (filter (_ > min-index)) data[x])
    | None -> false
    | Some([]) -> false
    | Some(new-min::_) -> subseq-loop subseq new-min
  in
    subseq-loop subseq -1

उदाहरण कार्यान्वयन (पर्ल):

use strict; use warnings; use signatures; use Test::More;

sub build_dict ($seq) {
  my %dict;
  while (my ($i, $x) = each @$seq) {
    push @{ $dict{$x} }, $i;
  }
  return \%dict;
}

sub is_subsequence_i ($seq, $subseq) {
  my $min_index = -1;
  my $dict = build_dict($seq);
  for my $x (@$subseq) {
    my $indices = $dict->{$x} or return 0;
    ($min_index) = grep { $_ > $min_index } @$indices or return 0;
  }
  return 1;
}

sub is_subsequence_f ($seq, $subseq) {
  my $dict = build_dict($seq);
  use feature 'current_sub';
  return sub ($subseq, $min_index) {
    return 1 if @$subseq == 0;
    my ($x, @subseq) = @$subseq;
    my ($new_min) = grep { $_ > $min_index } @{ $dict->{$x} // [] } or return 0;
    __SUB__->(\@subseq, $new_min);
  }->($subseq, -1);
}

my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];

for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
  ok $is_subsequence->($A, $B), 'B in A';
  ok $is_subsequence->($A, $C), 'C in A';
  ok ! $is_subsequence->($A, $D), 'D not in A';
  ok ! $is_subsequence->($A, $E), 'E not in A';
  ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}

done_testing;

शब्दकोश लुकअप वेरिएंट: एक परिमित राज्य मशीन के रूप में एन्कोडिंग

विवरण

यदि हम अधिक मेमोरी में ट्रेड करते हैं तो हम एल्गोरिदम की जटिलता को घटाकर O (सबसेक्साइज़) तक कम कर सकते हैं । तत्वों को उनके सूचकांक में मैप करने के बजाय, हम एक ग्राफ बनाते हैं जहां प्रत्येक नोड अपने सूचकांक में एक तत्व का प्रतिनिधित्व करता है। किनारों को संभव संक्रमण दिखाते हैं, जैसे अनुक्रम a, b, aमें किनारे होंगे a@1 → b@2, a@1 → a@3, b@2 → a@3। यह ग्राफ एक परिमित राज्य मशीन के बराबर है।

लुकअप के दौरान हम एक कर्सर रखते हैं जो शुरू में पेड़ का पहला नोड है। हम तब सबलिस्ट B में प्रत्येक तत्व के लिए किनारे चलते हैं । यदि ऐसी कोई धार मौजूद नहीं है, तो B कोई सबलिस्ट नहीं है। यदि सभी तत्वों के बाद कर्सर में एक वैध नोड होता है, तो बी एक सबलिस्ट है।

स्यूडोकोड

शाही शैली:

# preparing the graph
graph = {}
for x in seq.reverse:
  next_graph = graph.clone
  next_graph[x] = graph
  graph = next_graph

def subseq? subseq:
  cursor = graph
  for x in subseq:
    cursor = graph[x]
    return false if graph == null
  return true

कार्यात्मक शैली:

let subseq? subseq =
  let rec subseq-loop = function
  | [] _ -> true
  | x::subseq graph -> match (graph[x])
    | None -> false
    | Some(next-graph) -> subseq-loop subseq next-graph
  in
    subseq-loop subseq graph

उदाहरण कार्यान्वयन (पर्ल):

use strict; use warnings; use signatures; use Test::More;

sub build_graph ($seq) {
  my $graph = {};
  for (reverse @$seq) {
    $graph = { %$graph, $_ => $graph };
  }
  return $graph;
}

sub is_subsequence_i ($seq, $subseq) {
  my $cursor = build_graph($seq);
  for my $x (@$subseq) {
    $cursor = $cursor->{$x} or return 0;
  }
  return 1;
}

sub is_subsequence_f ($seq, $subseq) {
  my $graph = build_graph($seq);
  use feature 'current_sub';
  return sub ($subseq, $graph) {
    return 1 if @$subseq == 0;
    my ($x, @subseq) = @$subseq;
    my $next_graph = $graph->{$x} or return 0;
    __SUB__->(\@subseq, $next_graph);
  }->($subseq, $graph);
}

my $A = [1, 2, 3, 4];
my $B = [1, 3];
my $C = [1, 3, 4];
my $D = [3, 1];
my $E = [1, 2, 5];

for my $is_subsequence (\&is_subsequence_i, \&is_subsequence_f) {
  ok $is_subsequence->($A, $B), 'B in A';
  ok $is_subsequence->($A, $C), 'C in A';
  ok ! $is_subsequence->($A, $D), 'D not in A';
  ok ! $is_subsequence->($A, $E), 'E not in A';
  ok $is_subsequence->([1, 2, 3, 4, 3, 5, 6], [2, 3, 6]), 'multiple nums';
}

done_testing;

एक तरफ के रूप में, आपने कैसे studyकाम किया है और यदि एल्गोरिदम इसे लागू करता है तो यहां कुछ व्यावहारिक अनुप्रयोग हो सकता है?

1
@MichaelT मुझे यकीन नहीं है कि मैं समझता हूं ... मैं एक अंडरग्रेड हूं, लेकिन मुझे अभी तक पता नहीं चला है कि वास्तव में अध्ययन कैसे करना है </ मजाक>। यदि आप पर्ल बिलिन फ़ंक्शन के बारे में बात कर रहे हैं: यह आजकल एक नो-ऑप है। वर्तमान कार्यान्वयन बैकवर्ड संगतता की सिर्फ एक दर्जन लाइनें हैं। रेगेक्स इंजन सीधे ऐसी विशेषताओं को नियोजित करता है, जैसे कि चर-आकार के पैटर्न के मिलान से पहले निरंतर तारों की खोज करना। studyपहले चरित्र-से-स्थिति लुकअप तालिकाओं का निर्माण किया था, मेरे दूसरे समाधान के विपरीत नहीं।
आमोन

और भी बेहतर एल्गोरिथ्म के साथ अद्यतन
आमोन

उस FSM के बारे में अधिक बताते हुए, आप सभी परीक्षण अनुक्रमों को एक FSM में संकलित कर सकते हैं और फिर पूरे अनुक्रम के माध्यम से चला सकते हैं। इस बात पर निर्भर करता है कि आप अंत में किस अवस्था में थे, यह निर्धारित करता है कि बाद में कौन से मिलान किए गए थे। यह निश्चित रूप से वह चीज है जो किसी भी गैर-तुच्छ व्यक्ति के लिए कंप्यूटर के बजाय हाथ से करना होगा।

@MichaelT आप सही हैं कि हम इस तरह से एक पहचानकर्ता का निर्माण कर सकते हैं । हालाँकि, हम पहले से ही n · O (B) + की आरंभीकरण लागत O (f (A)) में हैं । सभी Bs की त्रि-जैसी संरचना के निर्माण में O (n · B) जैसा कुछ होगा , जिससे मिलान O (A) में होगा । यह सैद्धांतिक रूप से सस्ता होने का एक वास्तविक मौका है (3 डी समाधान में ग्राफ का निर्माण महंगा हो सकता है, लेकिन यह सिर्फ एक बार की लागत है)। मुझे लगता है कि ए B एन · बी के लिए एक ट्राइ बेहतर है , और इसका नुकसान यह है कि यह स्ट्रीमिंग इनपुट को संभाल नहीं सकता है - सभी बी एस को मिलान करने से पहले लोड किया जाना है। मैं शायद 6h में उत्तर को अपडेट करूंगा।
अमोन

6

यहां एक व्यावहारिक दृष्टिकोण है जो आपके स्वयं के एल्गोरिदम को लागू करने के "कठिन परिश्रम" से बचता है, और "पहिया को सुदृढ़ करने" से भी बचता है: समस्या के लिए एक नियमित अभिव्यक्ति इंजन का उपयोग करें ।

बस ए के सभी नंबरों को एक स्ट्रिंग में डालें, और बी के सभी नंबरों को नियमित अभिव्यक्ति द्वारा अलग किया जाए (.*)^शुरुआत में और $अंत में एक चरित्र जोड़ें । फिर अपने पसंदीदा नियमित अभिव्यक्ति इंजन को सभी मैचों के लिए खोज करने दें। उदाहरण के लिए, जब

ए = (1234356), बी = (236)

B के लिए एक reg exp बनाएं ^(.*)2(.*)3(.*)6(.*)$। अब एक वैश्विक regexp खोज चलाएँ। यह पता लगाने के लिए कि आपके बाद के मैच में कौन से स्थान पर हैं, पहले 3 उप-वर्गों की लंबाई की जांच करें।

यदि आपके पूर्णांक की सीमा 0 से 9 तक है, तो आप उन्हें इस कार्य को करने के लिए पहले एकल अक्षरों से सांकेतिक शब्दों में बदलना पर विचार कर सकते हैं, या आपको एक अलग चरित्र का उपयोग करके विचार को अनुकूलित करना होगा।

बेशक, उस दृष्टिकोण की गति आपके द्वारा उपयोग किए जा रहे रेग एक्सप इंजन की गति पर बहुत कुछ निर्भर करेगी, लेकिन अत्यधिक अनुकूलित इंजन उपलब्ध हैं, और मुझे लगता है कि "बॉक्स से बाहर" एक तेज एल्गोरिदम को लागू करना कठिन होगा। ।


एक regex और उसके इंजन को लागू करने के लिए सभी तरह से जाने की जरूरत नहीं है। इसे चलाने के लिए एक साधारण निर्धारक परिमित ऑटोमेटा का उपयोग करना संभव होगा। इसका 'सीधी रेखा' मार्ग है।

@MichaelT: ठीक है, मेरे पास "जेनेरिक परिमित ऑटोमेटा" पुस्तकालय नहीं है, और ओपी ने हमें उस प्रोग्रामिंग भाषा के बारे में नहीं बताया जिसका वह उपयोग करता है, लेकिन नियमित रूप से अभिव्यक्तियाँ आज लगभग हर गंभीर प्रोग्रामिंग भाषा के लिए उपलब्ध हैं। "। उदाहरण के लिए, अमोन के समाधान की तुलना में बहुत कम कोड के साथ, मेरे सुझाव को लागू करना बहुत आसान बनाना चाहिए। IMHO ओपी को यह कोशिश करनी चाहिए, अगर यह उसके लिए बहुत धीमी है, तो वह अभी भी कोशिश कर सकता है यदि एक अधिक जटिल समाधान उसे बेहतर सेवा देगा।
डॉक्टर ब्राउन

आप एक सामान्य पुस्तकालय की जरूरत नहीं है। आप सभी की जरूरत है 'पैटर्न' के सरणी और सरणी में सूचकांक के लिए एक सूचक है। सूचकांक अगले "मूल्य की तलाश" को इंगित करता है, और जब आप इसे स्रोत से पढ़ते हैं, तो सूचकांक बढ़ाते हैं। जब आप ऐरे के अंत में आते हैं, तो आप इसका मिलान कर लेते हैं। यदि आप स्रोत के अंत को बिना अंत तक पढ़ते हैं, तो आपने उसका मिलान नहीं किया है।

@ मिचेल्ट: फिर आप एक उत्तर के रूप में उस एल्गोरिथ्म का एक स्केच पोस्ट क्यों नहीं करते?
डॉक ब्राउन

ज्यादातर इसलिए कि इसका पहले से बेहतर उत्तर पहले से ही है - "हम अनुक्रम ए के लिए एक कर्सर बनाए रखते हैं। फिर हम बाद के बी में सभी वस्तुओं के माध्यम से पुनरावृत्ति करते हैं। प्रत्येक आइटम के लिए, हम ए को कर्सर को आगे बढ़ाते हैं जब तक कि हम एक मिलान आइटम नहीं मिला। यदि नहीं मिलान करने वाला आइटम मिला, तो B कोई अनुवर्ती नहीं है। "

0

यदि लंबाई प्राप्त करना और अनुक्रम को पुनरावृत्त करना कुशल है, तो यह एल्गोरिथ्म काफी कुशल होना चाहिए।

  1. दोनों क्रमों की लंबाई की तुलना करें। अब sequenceऔर छोटे को स्टोर करेंsubsequence
  2. दोनों दृश्यों और लूप की शुरुआत में अंत तक शुरू करें sequence
    1. की वर्तमान स्थिति पर संख्या के sequenceबराबर वर्तमान स्थिति में संख्या हैsubsequence
    2. यदि हाँ, दोनों स्थितियों को एक और आगे बढ़ाएँ
    3. यदि नहीं, तो केवल एक की स्थिति को sequenceआगे बढ़ाएं
  3. के subsequenceअंत में स्थिति हैsequence
  4. यदि हाँ, तो दो क्रम मेल खाते हैं
  5. यदि नहीं, तो दोनों क्रम मेल नहीं खाते हैं
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.