Ähnlichkeitsdetektor herausfordern

11

Herausforderung

Versuchen Sie anhand von zwei Frage-IDs herauszufinden, wie ähnlich sie sind, indem Sie sich die Antworten ansehen.

Einzelheiten

Sie erhalten zwei Frage-IDs für codegolf.stackexchange.com; Sie können davon ausgehen, dass für beide IDs Fragen vorhanden sind, die nicht gelöscht, aber nicht unbedingt offen sind. Sie müssen alle Antworten durchgehen und den Mindestabstand zwischen den Codes in den Antworten auf die beiden Fragen (ohne gelöschte Antworten) bestimmen. Das heißt, Sie sollten jede Antwort in Frage 1 mit jeder Antwort in Frage 2 vergleichen und den Mindestabstand von Levenshtein bestimmen. Gehen Sie wie folgt vor, um den Code in einer Antwort zu finden:

So finden Sie das Code-Snippet

Ein Textkörper ist der eigentliche Code der Antwort, wenn er sich in Backticks befindet und sich in einer eigenen Zeile befindet oder wenn er mit 4 Leerzeichen eingerückt ist und eine leere Zeile darüber steht, es sei denn, es befindet sich kein Text darüber.

Beispiele für gültige und ungültige Codefragmente (mit .Leerzeichen) (durch eine Tonne Gleichheitszeichen getrennt)

This is `not a valid code snippet because it is not on its own line`
========================================
This is:
`A valid code snippet`
========================================
This is
....not a valid code snippet because there's no spacing line above
========================================
This is

....A valid code snippet because there's a spacing line above
========================================
....Valid code snippet because there's no other text
========================================

Wenn die Antwort keine gültigen Codefragmente enthält, ignorieren Sie die Antwort vollständig. Beachten Sie, dass Sie nur den ersten Codeblock nehmen sollten.

Endgültige Spezifikationen

Die zwei Fragen-IDs können in einem beliebigen vernünftigen Format für 2 Ganzzahlen eingegeben werden. Die Ausgabe sollte der kleinste Levenshtein-Abstand zwischen zwei gültigen Antworten aus beiden Herausforderungen sein. Wenn es für eine oder beide Herausforderungen keine "gültigen" Antworten gibt, geben Sie aus -1.

Testfall

Für die Herausforderung 115715(Embedded Hexagons) und 116616(Embedded Triangles) beide von Comrade SparklePony hatten die beiden Charcoal-Antworten (beide von KritixiLithos) einen Levenshtein-Abstand von 23, was der kleinste war. Somit 115715, 116616wäre Ihre Ausgabe für 23.

Bearbeiten

Sie können davon ausgehen, dass die Frage aufgrund einer Einschränkung der API-Seitengröße höchstens 100 Antworten enthält. Sie sollten Backticks in Codeblöcken nicht ignorieren, nur wenn der Codeblock selbst mit Backticks und nicht in einer eigenen Zeile erstellt wird.

Bearbeiten

Ich habe die Kopfgeldperiode vorzeitig beendet, weil ich einen Mod um eine einwöchige Sperre gebeten habe und nicht wollte, dass die Kopfgeldgebühr automatisch an die Antwort mit der höchsten Punktzahl vergeben wird (die zufällig die längste ist). Wenn eine neue Einreichung eingeht oder eine Einreichung so hoch ist, dass sie vor dem tatsächlichen Ende des Kopfgeldzeitraums (UTC 00:00 am 1. Juni) kürzer als 532 Byte wird, werde ich diesem ein Kopfgeld geben, um mein Versprechen danach einzuhalten Die Aussetzung läuft ab. Wenn ich mich richtig erinnere, muss ich das nächste Mal die Kopfgeldperiode verdoppeln. Wenn Sie also eine Antwort erhalten, erhalten Sie möglicherweise +200 :)

HyperNeutrino
quelle
1
Ich bin verwirrt darüber, was als gültiges Code-Snippet gilt. Warum nicht einfach alles, was in <code> -Tags im HTML steht?
Calvins Hobbys
@HelkaHomba Was ist mit den Newline-Einschränkungen? Ich könnte versuchen, einen anderen Weg zu finden, um diese einzubeziehen.
HyperNeutrino
@HelkaHomba Wenn die Antwort einen durch Backticks getrennten Code in einer Zeile enthält, sollte dieser im Wesentlichen ignoriert werden.
HyperNeutrino
Dies ist eine dieser Antworten, bei denen es einfacher ist, den Hauptteil der Frage zu erledigen. Das Herunterladen der Seite und das Extrahieren der Codeblöcke ist schwieriger als die Entfernung von sieben Minuten.
Bálint
1
Cool. Ich überprüfe nur.
Matt

Antworten:

1

PowerShell, 532 Byte

$1,$2=$args
$a={irm "api.stackexchange.com/2.2/questions/$args/answers?pagesize=100&site=codegolf&filter=!9YdnSMKKT"|% i*}
$r={$args.body-replace"(?sm).*?^(<pre.*?>)?<code>(.*?)</code>.*",'$2'}
$1=&$a $1;$2=&$a $2
(0..($1.count-1)|%{
    $c=&$r $1[$_]
    0..($2.count-1)|%{
        &{$c,$d=$args;$e,$f=$c,$d|% le*;$m=[object[,]]::new($f+1,$e+1);0..$e|%{$m[0,$_]=$_};0..$f|%{$m[$_,0]=$_};1..$e|%{$i=$_;1..$f|%{$m[$_,$i]=(($m[($_-1),$i]+1),($m[$_,($i-1)]+1),($m[($_-1),($i-1)]+((1,0)[($c[($i-1)]-eq$d[($_-1)])]))|sort)[0]}};$m[$f,$e]} $c $d
    }
}|sort)[0]

Ich habe dort Zeilenumbrüche hinterlassen, um die Lesbarkeit zu verbessern. Die spiegeln sich immer noch in meiner Byteanzahl wider.

Ich bin mir ziemlich sicher, dass ich das im Griff habe. Das Schwierige für mich war, die Levenshtein-Distanz zu erreichen, da PowerShell meines Wissens keine eingebaute Funktion dafür hat. Dadurch konnte ich die damit verbundene Herausforderung auf Levenshtein Distanz beantworten . Wenn sich mein Code auf eine anonyme Funktion für LD bezieht, können Sie sich auf diese Antwort beziehen, um eine detailliertere Erklärung zu erhalten, wie dies funktioniert.

Code mit Kommentaren und Fortschrittsanzeige

Der Code kann sehr langsam werden (aufgrund der LD), daher habe ich einige Fortschrittsindikatoren für mich eingebaut, damit ich die Aktion verfolgen kann, während sie sich entfaltet, und nicht davon ausgehen kann, dass sie irgendwo in einer Schleife steckt. Der Code zur Überwachung des Fortschritts befindet sich weder im obersten Block noch wird er in meiner Byteanzahl gezählt.

# Assign the two integers into two variables. 
$1,$2=$args

# Quick function to download up to 100 of the answer object to a given question using the SE API
$a={irm "api.stackexchange.com/2.2/questions/$args/answers?pagesize=100&site=codegolf&filter=!9YdnSMKKT"|% i*}

# Quick function that takes the body (as HTML) of an answer and parses out the likely codeblock from it. 
$r={$args.body-replace"(?sm).*?^(<pre.*?>)?<code>(.*?)</code>.*",'$2'}

# Get the array of answers from the two questions linked.
$1=&$a $1;$2=&$a $2

# Hash table of parameters used for Write-Progress
# LD calcuations can be really slow on larger strings so I used this for testing so I knew 
# how much longer I needed to wait.
$parentProgressParameters = @{
    ID = 1 
    Activity = "Get LD of all questions" 
    Status = "Counting poppy seeds on the bagel"
}

$childProgressParameters = @{
    ID = 2
    ParentID = 1
    Status = "Progress"
}


# Cycle each code block from each answer against each answer in the other question.
(0..($1.count-1)|%{
    # Get the code block from this answer
    $c=&$r $1[$_]

    # Next line just for displaying progress. Not part of code. 
    Write-Progress @parentProgressParameters -PercentComplete (($_+1) / $1.count * 100) -CurrentOperation "Answer $($_+1) from question 1"

    0..($2.count-1)|%{
        # Get the code block from this answer   
        $d=&$r $2[$_]

        # Next two lines are for progress display. Not part of code. 
        $childProgressParameters.Activity = "Comparing answer $($_+1) of $($2.count)"
        Write-Progress @childProgressParameters -PercentComplete (($_+1) / $2.count * 100) -CurrentOperation "Answer $($_+1) from question 2"

        # Anonymous function to calculate Levenstien Distance
        # Get a better look at that function here: /codegolf//a/123389/52023
        &{$c,$d=$args;$e,$f=$c,$d|% le*;$m=[object[,]]::new($f+1,$e+1);0..$e|%{$m[0,$_]=$_};0..$f|%{$m[$_,0]=$_};1..$e|%{$i=$_;1..$f|%{$m[$_,$i]=(($m[($_-1),$i]+1),($m[$_,($i-1)]+1),($m[($_-1),($i-1)]+((1,0)[($c[($i-1)]-eq$d[($_-1)])]))|sort)[0]}};$m[$f,$e]} $c $d
    }
# Collect results and sort leaving the smallest number on top.
}|sort)[0]

Meine Logik zum Auffinden der Codeblöcke besteht darin, die Antwort als HTML zu verwenden und nach einem Code-Tag-Satz zu suchen, der optional von einem Pre-Tag-Satz umgeben ist, der in einer eigenen Zeile beginnt. Beim Testen wurden alle korrekten Daten zu 6 verschiedenen Fragensätzen gefunden.

Ich habe versucht, mit dem Markdown-Code zu arbeiten, aber es war zu schwierig, den richtigen Codeblock zu finden.

Probeläufe

Challenge-Similarity-Detector 97752 122740
57

Challenge-Similarity-Detector 115715 116616
23
Matt
quelle
Ich habe den größten Teil von 3 Tagen damit verbracht, mich damit zu beschäftigen. Diese Herausforderung ist in meinen Top 5 für den größten Spaß beim Versuch. TFTC (Danke für die Herausforderung)
Matt
Gut gemacht! Danke, ich bin froh, dass es dir gefallen hat! :)
HyperNeutrino
Hinweis: Ich habe das Kopfgeld früher als angegeben vergeben, weil ich eine Aussetzung beantrage, damit ich es später nicht mehr vergeben kann. Gut gemacht! :)
HyperNeutrino
Suspendierung beantragen?
Matt
Ja, ich habe Dennis gebeten, mich für eine Woche zu suspendieren, damit ich mich auf die Schularbeiten konzentrieren kann. Es wurde schon früher gemacht (obwohl ich immer noch hier bin ... ich weiß nicht, wann ich verschwinden werde).
HyperNeutrino
3

Java + Jsoup, 1027 Bytes

Die ersten beiden Argumente sind die Frage-IDs.

Golf:

import org.jsoup.*;import org.jsoup.nodes.*;class M{String a1[]=new String[100],a2[]=new String[100],c[];int i1=0,i2=0;public static void main(String a[])throws Exception{String r="/codegolf/";M m=new M();m.c=m.a1;m.r(Jsoup.connect(r+a[0]).get());m.c=m.a2;m.r(Jsoup.connect(r+a[1]).get());int s=m.ld(m.a1[1],m.a2[1]);for(int i=2;i<m.a1.length;i++)for(int j=2;j<m.a2.length;i++){if(m.a1[i]==null)break;int d=m.ld(m.a1[i],m.a2[j]);if(d<s)s=d;}System.out.print(s);}void r(Document d){a:for(Element e:d.select("td")){for(Element p:e.select("pre")){ a(p.select("code").get(0).html());continue a;}}}void a(String d){c[c==a1?i1++:i2++]=d;}int ld(String a,String b){a=a.toLowerCase();b=b.toLowerCase();int[]costs=new int[b.length()+1];for(int j=0;j<costs.length;j++)costs[j]=j;for(int i=1;i<=a.length();i++){costs[0]=i;int nw=i-1;for(int j=1;j<=b.length();j++){int cj=Math.min(1+Math.min(costs[j],costs[j-1]),a.charAt(i-1)==b.charAt(j-1)?nw:nw+1);nw=costs[j];costs[j]=cj;}}return costs[b.length()];}}

Lesbar:

import org.jsoup.*;import org.jsoup.nodes.*;

class M {
    String a1[]=new String[100],a2[]=new String[100],c[];
    int i1=0,i2=0;
    public static void main(String a[])throws Exception{
    String r="/codegolf/";
    M m=new M();

    m.c=m.a1;
    m.r(Jsoup.connect(r+a[0]).get());
    m.c=m.a2;
    m.r(Jsoup.connect(r+a[1]).get());

    int s=m.ld(m.a1[1],m.a2[1]);
    for(int i=2;i<m.a1.length;i++)for(int j=2;j<m.a2.length;i++){if(m.a1[i]==null)break;int d=m.ld(m.a1[i],m.a2[j]);if(d<s)s=d;}
    System.out.print(s);
}

void r(Document d) {
    a:for(Element e:d.select("td")) {for(Element p:e.select("pre")) { 
        a(p.select("code").get(0).html());
        continue a;
    }}
}

void a(String d){c[c==a1?i1++:i2++]=d;}

int ld(String a, String b) {
    a = a.toLowerCase();
    b = b.toLowerCase();
    int [] costs = new int [b.length() + 1];
    for (int j = 0; j < costs.length; j++)costs[j] = j;
    for (int i = 1; i <= a.length(); i++) {
        costs[0] = i;
        int nw = i - 1;
        for (int j = 1; j <= b.length(); j++) {
            int cj = Math.min(1 + Math.min(costs[j], costs[j - 1]), a.charAt(i - 1) == b.charAt(j - 1) ? nw : nw + 1);
            nw = costs[j];
            costs[j] = cj;
        }
    }
    return costs[b.length()];
}

}}

Tomahawk2001913
quelle
schlag mich dazu !!!! Nett!
Tuskiomi
1
Willkommen bei PPCG! Es verstößt nicht gegen die Regeln, eine Bibliothek eines Drittanbieters zu verwenden, aber wir fordern, dass die Verwendung der Bibliothek mit der Sprache vermerkt wird (daher würde eine Java-Antwort, die eine Bibliothek namens JavaHTML verwendet, als "Java + JavaHTML" bezeichnet).
Mego
OK danke! Das werde ich mir beim nächsten Mal merken!
Tomahawk2001913
Nichts hindert Sie daran, eine Bibliothek in dieser Herausforderung zu verwenden, wenn Sie möchten.
Matt
Ich muss jetzt vielleicht, dass jemand meine Antwort übertroffen hat!
Tomahawk2001913
0

Mathematica, 540 Bytes

f=Flatten;l=Length;P=StringPosition;(H[r_]:=Block[{s,a,t,k},t={};d=1;k="/codegolf/"<>r;s=First/@P[Import[k,"Text"],"<pre><code>"];a=f[First/@P[Import[k,"Text"],"answerCount"]][[1]];While[d<l@s,If[s[[d]]>a,AppendTo[t,s[[d]]]];d++];Table[StringDelete[StringCases[StringTake[Import[k,"Text"],{t[[i]],t[[i]]+200}],"<pre><code>"~~__~~"</code></pre>"],{"<pre><code>","</code></pre>"}],{i, l@t}]];Min@DeleteCases[f@Table[EditDistance[ToString@Row@H[#1][[i]],ToString@Row@H[#2][[j]]],{i,l@H[#1]},{j,l@H[#1]}],0])&


Eingang

[115715, 116616]

Ausgabe

23

verwendet die integrierte EditDistance, die "den Editier- oder Levenshtein-Abstand zwischen Strings oder Vektoren u und v angibt".

Wie für den Testfall mathematica

EditDistance["FN«AX²ιβ×__β↓↘β←↙β↑←×__β↖β→↗β","NαWα«X²ι↙AX²⁻ι¹β↙β↑↖β→A⁻α¹α"]

gibt 23 zurück

Ich denke, ich kann ein bisschen mehr Golf spielen. Das
Laufen dauert einige Minuten

J42161217
quelle