
Wir veröffentlichen weiterhin Analysen der Aufgaben, die bei der letzten Meisterschaft vorgeschlagen wurden. Als nĂ€chstes folgen Aufgaben aus der Qualifikationsrunde fĂŒr Spezialisten fĂŒr maschinelles Lernen. Dies ist der dritte von vier Tracks (Backend, Frontend, ML, Analytics). Die Teilnehmer mussten ein Modell fĂŒr die Korrektur von Tippfehlern in Texten erstellen, eine Strategie fĂŒr das Spielen an Spielautomaten vorschlagen, ein System von Empfehlungen fĂŒr Inhalte in Erinnerung rufen und mehrere weitere Programme erstellen.
A. Tippfehler
Zustand
(Epigraph) (aus einem Forum)
- Wer hat diesen Unsinn komponiert?
- Astrophysiker. Sie sind auch Menschen.
- Sie haben 10 Fehler im Wort "Journalisten" gemacht.
Viele Benutzer machen Tippfehler, einige aufgrund von TastenanschlĂ€gen und andere aufgrund ihres Analphabetismus. Wir möchten prĂŒfen, ob der Benutzer tatsĂ€chlich an ein anderes Wort als das von ihm eingegebene denken kann.
Nehmen wir formeller an, dass das folgende Fehlermodell auftritt: Der Benutzer beginnt mit einem Wort, das er schreiben möchte, und macht anschlieĂend eine Reihe von Fehlern. Jeder Fehler ist eine Ersetzung eines Teilstrings des Wortes durch einen anderen Teilstring. Ein Fehler entspricht dem Ersetzen nur an einer Position (dh wenn der Benutzer einen einzelnen Fehler durch die Regel "abc" â "cba" machen möchte, kann er aus der Zeichenfolge "abcabc" entweder "cbaabc" oder "abccba" erhalten). Nach jedem Fehler wird der Vorgang wiederholt. Dieselbe Regel könnte mehrmals in verschiedenen Schritten verwendet werden (im obigen Beispiel könnte beispielsweise "cbacba" in zwei Schritten erhalten werden).
Es ist erforderlich, die Mindestanzahl von Fehlern zu bestimmen, die ein Benutzer machen kann, wenn er ein bestimmtes Wort im Auge hat und ein anderes schreibt.
E / A-Formate und BeispielEingabeformat
Die erste Zeile enthĂ€lt das Wort, das der Benutzer nach unserer Annahme im Sinn hatte (es besteht aus Buchstaben des lateinischen Alphabets in Kleinbuchstaben, die LĂ€nge ĂŒberschreitet 20 nicht).
Die zweite Zeile enthĂ€lt das Wort, das er tatsĂ€chlich geschrieben hat (es besteht auch aus Buchstaben des lateinischen Alphabets in Kleinbuchstaben, die LĂ€nge ĂŒberschreitet 20 nicht).
Die dritte Zeile enthÀlt eine einzelne Zahl N (N <50) - die Anzahl der Ersetzungen, die verschiedene Fehler beschreiben.
Die nĂ€chsten N Zeilen enthalten mögliche Ersetzungen im Format & lt "korrekte" Buchstabenfolge & gt <Leerzeichen> <"fehlerhafte" Buchstabenfolge>. Sequenzen dĂŒrfen nicht lĂ€nger als 6 Zeichen sein.
Ausgabeformat
Es ist erforderlich, eine Zahl zu drucken - die Mindestanzahl von Fehlern, die der Benutzer machen kann. Wenn diese Zahl 4 ĂŒberschreitet oder es unmöglich ist, aus einem Wort ein anderes zu erhalten, drucken Sie -1.
Beispiel
Lösung
Versuchen wir, aus der richtigen Schreibweise alle möglichen Wörter mit nicht mehr als 4 Fehlern zu generieren. Im schlimmsten Fall kann O ((LïčN)
4 ) vorliegen. In den Grenzen des Problems ist dies eine ziemlich groĂe Zahl, daher mĂŒssen Sie herausfinden, wie Sie die KomplexitĂ€t reduzieren können. Stattdessen können Sie den Meet-in-the-Middle-Algorithmus verwenden: Generieren Sie Wörter mit nicht mehr als 2 Fehlern sowie Wörter, aus denen Sie ein vom Benutzer geschriebenes Wort mit nicht mehr als 2 Fehlern erhalten können. Beachten Sie, dass die GröĂe jedes dieser SĂ€tze 10
6 nicht ĂŒberschreitet. Wenn die Anzahl der vom Benutzer gemachten Fehler 4 nicht ĂŒberschreitet, ĂŒberschneiden sich diese SĂ€tze. Ebenso können wir ĂŒberprĂŒfen, ob die Anzahl der Fehler 3, 2 und 1 nicht ĂŒberschreitet.
struct FromTo { std::string from; std::string to; }; std::pair<size_t, std::string> applyRule(const std::string& word, const FromTo &fromTo, int pos) { while(true) { int from = word.find(fromTo.from, pos); if (from == std::string::npos) { return {std::string::npos, {}}; } int to = from + fromTo.from.size(); auto cpy = word; for (int i = from; i < to; i++) { cpy[i] = fromTo.to[i - from]; } return {from, std::move(cpy)}; } } void inverseRules(std::vector<FromTo> &rules) { for (auto& rule: rules) { std::swap(rule.from, rule.to); } } int solve(std::string& wordOrig, std::string& wordMissprinted, std::vector<FromTo>& replaces) { std::unordered_map<std::string, int> mapping; std::unordered_map<int, std::string> mappingInverse; mapping.emplace(wordOrig, 0); mappingInverse.emplace(0, wordOrig); mapping.emplace(wordMissprinted, 1); mappingInverse.emplace(1, wordMissprinted); std::unordered_map<int, std::unordered_set<int>> edges; auto buildGraph = [&edges, &mapping, &mappingInverse](int startId, const std::vector<FromTo>& replaces, bool dir) { std::unordered_set<int> mappingLayer0; mappingLayer0 = {startId}; for (int i = 0; i < 2; i++) { std::unordered_set<int> mappingLayer1; for (const auto& v: mappingLayer0) { auto& word = mappingInverse.at(v); for (auto& fromTo: replaces) { size_t from = 0; while (true) { auto [tmp, wordCpy] = applyRule(word, fromTo, from); if (tmp == std::string::npos) { break; } from = tmp + 1; { int w = mapping.size(); mapping.emplace(wordCpy, w); w = mapping.at(wordCpy); mappingInverse.emplace(w, std::move(wordCpy)); if (dir) { edges[v].emplace(w); } else { edges[w].emplace(v); } mappingLayer1.emplace(w); } } } } mappingLayer0 = std::move(mappingLayer1); } }; buildGraph(0, replaces, true); inverseRules(replaces); buildGraph(1, replaces, false); { std::queue<std::pair<int, int>> q; q.emplace(0, 0); std::vector<bool> mask(mapping.size(), false); int level{0}; while (q.size()) { auto [w, level] = q.front(); q.pop(); if (mask[w]) { continue; } mask[w] = true; if (mappingInverse.at(w) == wordMissprinted) { return level; } for (auto& v: edges[w]) { q.emplace(v, level + 1); } } } return -1; }
B. Vielarmiger Bandit
Zustand
Dies ist eine interaktive Aufgabe.
Sie selbst wissen nicht, wie es passiert ist, aber Sie befanden sich in einer Halle mit Spielautomaten mit einer ganzen TĂŒte Token. Leider lehnen sie es an der Abendkasse ab, Token zurĂŒckzunehmen, und Sie haben beschlossen, Ihr GlĂŒck zu versuchen. Es gibt viele Spielautomaten in der Halle, die Sie spielen können. FĂŒr ein Spiel mit einem Spielautomaten verwenden Sie einen Token. Im Falle eines Gewinns gibt Ihnen die Maschine einen Dollar, im Falle eines Verlustes - nichts. Jede Maschine hat eine feste Gewinnwahrscheinlichkeit (die Sie nicht kennen), die jedoch fĂŒr verschiedene Maschinen unterschiedlich ist. Nachdem Sie die Website des Herstellers dieser Maschinen studiert haben, haben Sie festgestellt, dass die Gewinnwahrscheinlichkeit fĂŒr jede Maschine in der Herstellungsphase zufĂ€llig aus der
Beta-Verteilung mit bestimmten Parametern ausgewÀhlt wird.
Sie möchten Ihre erwarteten Gewinne maximieren.
E / A-Formate und BeispielEingabeformat
Eine AusfĂŒhrung kann aus mehreren Tests bestehen.
Jeder Test beginnt mit der Tatsache, dass Ihr Programm in der Zeile zwei durch ein Leerzeichen getrennte Ganzzahlen enthÀlt: Die Anzahl N ist die Anzahl der Token in Ihrer Tasche und M ist die Anzahl der Maschinen in der Halle (N †10
4 , M †min (N, 100)). ) Die nĂ€chste Zeile enthĂ€lt zwei reelle Zahlen α und ÎČ (1 †α, ÎČ â€ 10) - die Parameter der Beta-Verteilung der Gewinnwahrscheinlichkeit.
Das Kommunikationsprotokoll mit dem PrĂŒfsystem lautet wie folgt: Sie stellen genau N Anforderungen. Drucken Sie fĂŒr jede Anforderung in einer separaten Zeile die Nummer der Maschine aus, die Sie spielen möchten (von 1 bis einschlieĂlich M). Als Antwort wird in einer separaten Zeile entweder "0" oder "1" angezeigt, was jeweils einen Verlust und einen Gewinn in einem Spiel mit dem angeforderten Spielautomaten bedeutet.
Nach dem letzten Test gibt es anstelle der Zahlen N und M zwei Nullen.
Ausgabeformat
Die Aufgabe gilt als erledigt, wenn Ihre Entscheidung nicht viel schlimmer ist als die Entscheidung der Jury. Wenn Ihre Entscheidung erheblich schlechter ist als die Entscheidung der Jury, erhalten Sie das Urteil âfalsche Antwortâ.
Es ist garantiert, dass die Wahrscheinlichkeit, das Urteil âfalsche Antwortâ zu erhalten,
10-6 nicht ĂŒberschreitet, wenn Ihre Entscheidung nicht schlechter ist als die Entscheidung der Jury.
Anmerkungen
Interaktionsbeispiel:
____________________ stdin stdout ____________________ ____________________ 5 2 2 2 2 1 1 0 1 1 2 1 2 1
Lösung
Dieses Problem ist bekannt und kann auf verschiedene Arten gelöst werden. Die Hauptentscheidung der Jury setzte die
Thompson-Stichprobenstrategie um. Da jedoch die Anzahl der Schritte zu Beginn des Programms bekannt war, gibt es optimalere Strategien (z. B. UCB1). DarĂŒber hinaus könnte man sogar mit der Epsilon-Greedy-Strategie auskommen: Mit einer bestimmten Wahrscheinlichkeit Δ eine zufĂ€llige Maschine spielen und mit einer Wahrscheinlichkeit (1 - Δ) eine Maschine mit der besten Siegesstatistik spielen.
class SolverFromStdIn(object): def __init__(self): self.regrets = [0.] self.total_win = [0.] self.moves = [] class ThompsonSampling(SolverFromStdIn): def __init__(self, bandits_total, init_a=1, init_b=1): """ init_a (int): initial value of a in Beta(a, b). init_b (int): initial value of b in Beta(a, b). """ SolverFromStdIn.__init__(self) self.n = bandits_total self.alpha = init_a self.beta = init_b self._as = [init_a] * self.n
C. Ausrichtung der SĂ€tze
Zustand
Eine der wichtigsten Aufgaben fĂŒr das Training eines guten maschinellen Ăbersetzungsmodells ist ein guter Fall von parallelen SĂ€tzen. In der Regel sind parallele Angebote die Quelle fĂŒr parallele Angebote. Es stellt sich heraus, dass Sie hĂ€ufig nur ihre LĂ€nge kennen mĂŒssen, um ein bestimmtes Korpus paralleler SĂ€tze zu bilden. Insbesondere stellen Sie möglicherweise fest, dass der Satz in der Ausgangssprache umso lĂ€nger ĂŒbersetzt wird, je lĂ€nger er ist. Einige Schwierigkeiten liegen in der Tatsache, dass sich wĂ€hrend der Ăbersetzung die Anzahl der SĂ€tze im Text Ă€ndern kann: Manchmal können zwei benachbarte SĂ€tze in der Ăbersetzung zu einem kombiniert werden oder umgekehrt - ein Satz kann in zwei SĂ€tze geteilt werden. In einigen seltenen FĂ€llen können SĂ€tze in einer Ăbersetzung vollstĂ€ndig weggelassen werden, oder eine Ăbersetzung kann in einer Ăbersetzung erscheinen, die nicht im Original enthalten war.
Nehmen wir formeller an, dass das folgende generative Modell fĂŒr parallele GehĂ€use wahr ist. Bei jedem Schritt fĂŒhren wir einen der folgenden Schritte aus:
1. Stoppen SieMit der Wahrscheinlichkeit p
h endet
die Erzeugung der RĂŒmpfe.
2. [1-0] Angebote ĂŒberspringenMit der Wahrscheinlichkeit p
d schreiben
wir dem Originaltext einen Satz zu. Wir schreiben der Ăbersetzung nichts zu. Die LĂ€nge des Satzes in der Originalsprache L â„ 1 wird aus der diskreten Verteilung ausgewĂ€hlt:

.
Hier sind
ÎŒs ,
Ïs die Verteilungsparameter und
αs der so gewÀhlte Normalisierungskoeffizient

.
3. [0-1] Vorschlag einfĂŒgenMit der Wahrscheinlichkeit p
i weisen
wir der Ăbersetzung einen Satz zu. Wir schreiben dem Original nichts zu. Die LĂ€nge eines Satzes in einer Ăbersetzungssprache L â„ 1 wird aus einer diskreten Verteilung ausgewĂ€hlt:

.
Hier sind
Ό t ,
Ï t die Verteilungsparameter und
α t der so gewÀhlte Normalisierungskoeffizient

.
4. ĂbersetzungMit der Wahrscheinlichkeit (1 - p
d - p
i - p
h ) nehmen wir die LĂ€nge des Satzes in der Originalsprache L
s â„ 1 aus der Verteilung p
s (mit Aufrundung). Als nĂ€chstes erzeugen wir die LĂ€nge des Satzes in der Ăbersetzungssprache L
t â„ 1 aus der bedingten diskreten Verteilung:

.
Hier ist
α st der Normalisierungskoeffizient, und die verbleibenden Parameter sind in den vorhergehenden AbsÀtzen beschrieben.
Weiter ist ein weiterer Schritt:
1. [2-1] Mit der Wahrscheinlichkeit p
split s teilt sich
der erzeugte Satz in der Originalsprache in zwei nicht leere auf, so dass sich die Gesamtzahl der Wörter
um genau eins erhöht . Die Wahrscheinlichkeit, dass ein Satz der LÀnge L
s in Teile der LĂ€nge L
1 und L
2 zerfÀllt (dh L
1 + L
2 = L
s + 1), ist proportional zu P
s (L
1 ) â
P
s (L
2 ).
2. [1-2] Mit der Wahrscheinlichkeit p
split t teilt sich
der erzeugte Satz in der Zielsprache in zwei nicht leere SÀtze auf, so dass sich die Gesamtzahl der Wörter um genau eins erhöht. Die Wahrscheinlichkeit, dass ein Satz der LÀnge L
t in Teile der LÀnge L1 und L2 zerfÀllt (dh L
1 + L
2 = L
t + 1), ist proportional zu P
t (L
1 ) â
P
t (L
2 ).
3. 3. [1-1] Mit einer Wahrscheinlichkeit von (1 - p
split s - p
split t ) zerfÀllt keiner der generierten SÀtze.
E / A-Formate, Beispiele und HinweiseEingabeformat
Die erste Zeile der Datei enthÀlt die Verteilungsparameter: p
h , p
d , p
i , p
split s , p
split t , Ό
s , Ï
s , Ό
t , Ï
t . 0,1 †Ï
s <Ï
t †3. 0 †Ό
s , Ό
t †5.
Die nÀchste Zeile enthÀlt die Zahlen N
s und N
t - die Anzahl der SÀtze im Fall in der Originalsprache bzw. in der Zielsprache (1 †N
s , N
t †1000).
Die nÀchste Zeile enthÀlt N
s Ganzzahlen - die LÀnge der SÀtze in der Originalsprache. Die nÀchste Zeile enthÀlt N
t Ganzzahlen - die LĂ€nge der SĂ€tze in der Zielsprache.
Die nÀchste Zeile enthÀlt zwei Zahlen: j und k (1 †j †N
s , 1 †k †N
t ).
Ausgabeformat
Es ist erforderlich, die Wahrscheinlichkeit abzuleiten, dass SĂ€tze mit den Indizes j bzw. k in den Texten parallel sind (dh, dass sie in einem Schritt des Algorithmus erzeugt werden und keiner von ihnen das Ergebnis des Zerfalls ist).
Ihre Antwort wird akzeptiert, wenn der absolute Fehler 10
â4 nicht ĂŒberschreitet.
Beispiel 1
Beispiel 2
Beispiel 3
Anmerkungen
Im ersten Beispiel kann die anfÀngliche Folge von Zahlen auf drei Arten erhalten werden:
âą FĂŒgen Sie zuerst mit der Wahrscheinlichkeit p
d einen Satz zum Originaltext hinzu, dann mit der Wahrscheinlichkeit p
i einen Satz zur Ăbersetzung und beenden Sie dann mit der Wahrscheinlichkeit p
h die Generierung.
Die Wahrscheinlichkeit dieses Ereignisses ist P
1 = p
d * P
s (4) * p
i * P
t (20) * p
h .
âą FĂŒgen Sie zuerst mit der Wahrscheinlichkeit p
d einen Satz zum Originaltext hinzu, dann mit der Wahrscheinlichkeit p
i einen Satz zur Ăbersetzung und beenden Sie dann mit der Wahrscheinlichkeit p
h die Generierung.
Die Wahrscheinlichkeit dieses Ereignisses ist gleich P
2 = p
i * P
t (20) * p
d * P
s (4) * p
h .
âą Mit der Wahrscheinlichkeit (1 - p
h - p
d - p
i ) zwei SĂ€tze erzeugen, dann mit der Wahrscheinlichkeit (1 - p
split s - p
split t ) alles so lassen, wie es ist (dh das Original oder die Ăbersetzung nicht in zwei SĂ€tze teilen ) und beenden Sie danach mit der Wahrscheinlichkeit p
h die Erzeugung.
Die Wahrscheinlichkeit dieses Ereignisses ist

.
Als Ergebnis wird die Antwort berechnet als

.
Lösung
Die Aufgabe ist ein Sonderfall der Ausrichtung mit Hidden-Markov-Modellen (HMM-Ausrichtung). Die Hauptidee ist, dass Sie die Wahrscheinlichkeit berechnen können, mit diesem Modell und
dem VorwÀrtsalgorithmus ein bestimmtes Dokumentpaar zu generieren: In diesem Fall ist der Status ein Paar von DokumentprÀfixen. Dementsprechend kann die erforderliche Wahrscheinlichkeit der Ausrichtung eines bestimmten Paares paralleler SÀtze durch den
VorwĂ€rts-RĂŒckwĂ€rts- Algorithmus berechnet werden.
Code #include <iostream> #include <iomanip> #include <cmath> #include <vector> double p_h, p_d, p_i, p_tr, p_ss, p_st, mu_s, sigma_s, mu_t, sigma_t; double lognorm_cdf(double x, double mu, double sigma) { if (x < 1e-9) return 0.0; double res = std::log(x) - mu; res /= std::sqrt(2.0) * sigma; res = 0.5 * (1 + std::erf(res)); return res; } double length_probability(int l, double mu, double sigma) { return lognorm_cdf(l, mu, sigma) - lognorm_cdf(l - 1, mu, sigma); } double translation_probability(int ls, int lt) { double res = length_probability(ls, mu_s, sigma_s); double mu = mu_t - mu_s + std::log(ls); double sigma = std::sqrt(sigma_t * sigma_t - sigma_s * sigma_s); res *= length_probability(lt, mu, sigma); return res; } double split_probability(int l1, int l2, double mu, double sigma) { int l_sum = l1 + l2; double total_prob = 0.0; for (int i = 1; i < l_sum; ++i) { total_prob += length_probability(i, mu, sigma) * length_probability(l_sum - i, mu, sigma); } return length_probability(l1, mu, sigma) * length_probability(l2, mu, sigma) / total_prob; } double log_prob10(int ls) { return std::log(p_d * length_probability(ls, mu_s, sigma_s)); } double log_prob01(int lt) { return std::log(p_i * length_probability(lt, mu_t, sigma_t)); } double log_prob11(int ls, int lt) { return std::log(p_tr * (1 - p_ss - p_st) * translation_probability(ls, lt)); } double log_prob21(int ls1, int ls2, int lt) { return std::log(p_tr * p_ss * split_probability(ls1, ls2, mu_s, sigma_s) * translation_probability(ls1 + ls2 - 1, lt)); } double log_prob12(int ls, int lt1, int lt2) { return std::log(p_tr * p_st * split_probability(lt1, lt2, mu_t, sigma_t) * translation_probability(ls, lt1 + lt2 - 1)); } double logsum(double v1, double v2) { double res = std::max(v1, v2); v1 -= res; v2 -= res; v1 = std::min(v1, v2); if (v1 < -30) { return res; } return res + std::log(std::exp(v1) + 1.0); } double loginc(double* to, double from) { *to = logsum(*to, from); } constexpr double INF = 1e25; int main(void) { using std::cin; using std::cout; cin >> p_h >> p_d >> p_i >> p_ss >> p_st >> mu_s >> sigma_s >> mu_t >> sigma_t; p_tr = 1.0 - p_h - p_d - p_i; int Ns, Nt; cin >> Ns >> Nt; using std::vector; vector<int> ls(Ns), lt(Nt); for (int i = 0; i < Ns; ++i) cin >> ls[i]; for (int i = 0; i < Nt; ++i) cin >> lt[i]; vector< vector< double> > fwd(Ns + 1, vector<double>(Nt + 1, -INF)), bwd = fwd; fwd[0][0] = 0; bwd[Ns][Nt] = 0; for (int i = 0; i <= Ns; ++i) { for (int j = 0; j <= Nt; ++j) { if (i >= 1) { loginc(&fwd[i][j], fwd[i - 1][j] + log_prob10(ls[i - 1])); loginc(&bwd[Ns - i][Nt - j], bwd[Ns - i + 1][Nt - j] + log_prob10(ls[Ns - i])); } if (j >= 1) { loginc(&fwd[i][j], fwd[i][j - 1] + log_prob01(lt[j - 1])); loginc(&bwd[Ns - i][Nt - j], bwd[Ns - i][Nt - j + 1] + log_prob01(lt[Nt - j])); } if (i >= 1 && j >= 1) { loginc(&fwd[i][j], fwd[i - 1][j - 1] + log_prob11(ls[i - 1], lt[j - 1])); loginc(&bwd[Ns - i][Nt - j], bwd[Ns - i + 1][Nt - j + 1] + log_prob11(ls[Ns - i], lt[Nt - j])); } if (i >= 2 && j >= 1) { loginc(&fwd[i][j], fwd[i - 2][j - 1] + log_prob21(ls[i - 1], ls[i - 2], lt[j - 1])); loginc(&bwd[Ns - i][Nt - j], bwd[Ns - i + 2][Nt - j + 1] + log_prob21(ls[Ns - i], ls[Ns - i + 1], lt[Nt - j])); } if (i >= 1 && j >= 2) { loginc(&fwd[i][j], fwd[i - 1][j - 2] + log_prob12(ls[i - 1], lt[j - 1], lt[j - 2])); loginc(&bwd[Ns - i][Nt - j], bwd[Ns - i + 1][Nt - j + 2] + log_prob12(ls[Ns - i], lt[Nt - j], lt[Nt - j + 1])); } } } int j, k; cin >> j >> k; double rlog = fwd[j - 1][k - 1] + bwd[j][k] + log_prob11(ls[j - 1], lt[k - 1]) - bwd[0][0]; cout << std::fixed << std::setprecision(12) << std::exp(rlog) << std::endl; }
D. Band mit Empfehlungen
Zustand
Betrachten Sie einen Feed mit Empfehlungen fĂŒr heterogene Inhalte. Es mischt Objekte verschiedener Typen (Bilder, Videos, Nachrichten usw.). Diese Objekte sind normalerweise nach Relevanz fĂŒr den Benutzer geordnet: Je relevanter (interessanter) das Objekt fĂŒr den Benutzer ist, desto nĂ€her am Anfang der Empfehlungsliste. Bei einer solchen Reihenfolge treten jedoch hĂ€ufig Situationen auf, in denen mehrere Objekte desselben Typs in der Liste der Empfehlungen aufgefĂŒhrt sind. Dies verschlechtert die externe Vielfalt unserer Empfehlungen erheblich und daher gefĂ€llt es den Benutzern nicht. Es ist erforderlich, einen Algorithmus zu implementieren, der gemÀà der Liste der Empfehlungen eine neue Liste erstellt, die frei von diesem Problem ist und am relevantesten ist.
Eine erste Liste von Empfehlungen sei a = [a
0 , a
1 , ..., a
n - 1 ] mit der LĂ€nge n> 0. Ein Objekt mit der Nummer i hat den Typ mit der Nummer b
i â {0, ..., m - 1}. ZusĂ€tzlich hat ein Objekt unter der Nummer i die Relevanz r (a
i ) = 2
âi . Betrachten Sie die Liste, die aus der ersten Liste erhalten wird, indem Sie eine Teilmenge von Objekten auswĂ€hlen und neu anordnen: x = [a
i 0 , a
i 1 , ..., a
i k - 1 ] mit der LĂ€nge k (0 †k †n). Eine Liste wird als zulĂ€ssig bezeichnet, wenn keine zwei aufeinanderfolgenden Objekte in ihrem Typ ĂŒbereinstimmen, d. H. B
i j â b
i j + 1 fĂŒr alle j = 0, ..., k - 2. Die Relevanz der Liste wird durch die Formel berechnet
. Sie mĂŒssen die Liste der maximalen Relevanz unter allen gĂŒltigen finden.
E / A-Formate und BeispieleEingabeformat
In der ersten Zeile werden die Zahlen n und m mit einem Leerzeichen geschrieben (1 †n †100000, 1 †m †n). Die nÀchsten n Zeilen enthalten die Zahlen b
i fĂŒr i = 0, ..., n - 1 (0 †b
i †m - 1).
Ausgabeformat
Notieren Sie mit einem Leerzeichen die Anzahl der Objekte in der endgĂŒltigen Liste: i
0 , i
1 , ..., i
k - 1 .
Beispiel 1
Beispiel 2
Beispiel 3
Lösung
Mit einfachen mathematischen Berechnungen kann gezeigt werden, dass das Problem durch einen âgierigenâ Ansatz gelöst werden kann, dh in der optimalen Liste von Empfehlungen hat jeder Punkt das relevanteste Objekt von allen, die am selben Anfang der Liste gĂŒltig sind. Die Implementierung dieses Ansatzes ist einfach: Wir nehmen Objekte in einer Reihe und fĂŒgen sie, wenn möglich, der Antwort hinzu. Wenn ein ungĂŒltiges Objekt gefunden wird (dessen Typ mit dem Typ des vorherigen ĂŒbereinstimmt), legen wir es in einer separaten Warteschlange beiseite, aus der wir es so schnell wie möglich in die Antwort einfĂŒgen. Beachten Sie, dass zu jedem Zeitpunkt alle Objekte in dieser Warteschlange einen ĂŒbereinstimmenden Typ haben. Am Ende verbleiben möglicherweise mehrere Objekte in der Warteschlange. Sie werden nicht in die Antwort aufgenommen.
std::vector<int> blend(int n, int m, const std::vector<int>& types) { std::vector<int> result; std::queue<int> repeated; for (int i = 0; i < n; ++i) { if (result.empty() || types[result.back()] != types[i]) { result.push_back(i); if (!repeated.empty() && types[repeated.front()] != types[result.back()]) { result.push_back(repeated.front()); repeated.pop(); } } else { repeated.push(i); } } return result; }
D. Clusterisierung von Zeichenfolgen
Es gibt ein endliches Alphabet A = {a
1 , a
2 , ..., a
K - 1 , a
K = S}, a
i â {a, b, ..., z}, S ist das Ende der Zeile.
Betrachten Sie die folgende Methode zum Generieren von zufĂ€lligen Zeichenfolgen ĂŒber dem Alphabet A:
1. Das erste Zeichen x
1 ist eine Zufallsvariable mit der Verteilung P (x
1 = a
i ) = q
i (es ist bekannt, dass q
K = 0 ist).
2. Jedes nÀchste Zeichen wird basierend auf dem vorherigen gemÀà der bedingten Verteilung P (x
i = a
j || x
i - 1 = a
l ) = p
jl erzeugt .
3. Wenn x
i = S ist, stoppt die Erzeugung und das Ergebnis ist x
1 x
2 ... x
i - 1 .
Der Satz von Linien, die aus einer Mischung von zwei beschriebenen Modellen mit unterschiedlichen Parametern erzeugt werden, ist angegeben. FĂŒr jede Zeile muss der Index der Kette angegeben werden, aus der sie generiert wurde.
E / A-Formate, Beispiel und NotizenEingabeformat
Die erste Zeile enthĂ€lt zwei Zahlen 1000 †N †2000 und 3 †K †27 - die Anzahl der Zeilen bzw. die GröĂe des Alphabets.
Die zweite Zeile enthÀlt eine Zeile, die aus K - 1 verschiedenen Kleinbuchstaben des lateinischen Alphabets besteht und die ersten K - 1 Elemente des Alphabets angibt.
Jede der folgenden N Zeilen wird gemÀà dem in der Bedingung beschriebenen Algorithmus erzeugt.
Ausgabeformat
In n Zeilen enthĂ€lt die i-te Zeile die Clusternummer (0/1) fĂŒr die Sequenz in der i + 1-ten Zeile der Eingabedatei. Die Ăbereinstimmung mit der wahren Antwort sollte mindestens 80% betragen.
Beispiel
Anmerkungen
Hinweis zum Test aus der Bedingung: Darin werden die ersten 50 Zeilen aus der Verteilung generiert
P (x
i = a | x
i - 1 = a) = 0,5, P (x
i = S | x
i - 1 = a) = 0,5, P (x
1 = a) = 1; zweite 50 - aus der Verteilung
P (x
i = b | x
i - 1 = b) = 0,5, P (x
i = S | x
i - 1 = b) = 0,5, P (x
1 = b) = 1.
Lösung
Das Problem wird mithilfe des
EM-Algorithmus gelöst: Es wird angenommen, dass die dargestellte Probe aus einer Mischung von zwei Markov-Ketten generiert wird, deren Parameter wĂ€hrend der Iterationen wiederhergestellt werden. Eine EinschrĂ€nkung von 80% der richtigen Antworten wird vorgenommen, damit die Richtigkeit der Lösung nicht durch Beispiele beeintrĂ€chtigt wird, die in beiden Ketten eine hohe Wahrscheinlichkeit haben. Diese Beispiele können daher bei ordnungsgemĂ€Ăer Wiederherstellung einer Kette zugewiesen werden, die hinsichtlich der generierten Antwort falsch ist.
import random import math EPS = 1e-9 def empty_row(size): return [0] * size def empty_matrix(rows, cols): return [empty_row(cols) for _ in range(rows)] def normalized_row(row): row_sum = sum(row) + EPS return [x / row_sum for x in row] def normalized_matrix(mtx): return [normalized_row(r) for r in mtx] def restore_params(alphabet, string_samples): n_tokens = len(alphabet) n_samples = len(string_samples) samples = [tuple([alphabet.index(token) for token in s] + [n_tokens - 1, n_tokens - 1]) for s in string_samples] probs = [random.random() for _ in range(n_samples)] for _ in range(200): old_probs = [x for x in probs]
.