Détecteur de similitude de défi

11

Défi

Étant donné deux identifiants de questions, essayez de déterminer leur similitude en consultant les réponses.

Détails

Vous recevrez deux identifiants de questions pour codegolf.stackexchange.com; vous pouvez supposer qu'il existe des questions pour les deux ID qui ne sont pas supprimées, mais ne sont pas nécessairement ouvertes. Vous devez parcourir toutes les réponses et déterminer la distance Levenshtein minimale entre le code dans les réponses aux deux questions (à l'exclusion des réponses supprimées). Autrement dit, vous devez comparer chaque réponse de la question 1 à chaque réponse de la question 2 et déterminer la distance minimale de Levenshtein. Pour trouver le code dans une réponse, suivez la procédure suivante:

Comment trouver l'extrait de code

Un corps de texte est le code réel de la réponse s'il est en guillemets et se trouve sur sa propre ligne, ou s'il est en retrait de 4 espaces, avec une ligne vide au-dessus, sauf s'il n'y a pas de texte au-dessus.

Exemples d'extraits de code valides et non valides (avec .comme espace) (séparés par une tonne de signes égaux)

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
========================================

S'il n'y a pas d'extraits de code valides dans la réponse, ignorez complètement la réponse. Notez que vous ne devez prendre que le premier bloc de code.

Spécifications finales

Les deux ID de question peuvent être saisis dans n'importe quel format raisonnable pour 2 entiers. La sortie doit être la plus petite distance Levenshtein entre deux réponses valides de l'un ou l'autre défi. S'il n'y a pas de réponses «valides» pour l'un des défis ou les deux, sortez -1.

Cas de test

Pour les défis 115715(Embedded Hexagons) et 116616(Embedded Triangles) tous deux par le camarade SparklePony, les deux réponses au charbon (toutes deux par KritixiLithos) avaient une distance Levenshtein de 23, qui était la plus petite. Ainsi, votre sortie pour 115715, 116616serait 23.

Éditer

Vous pouvez supposer que la question a au plus 100 réponses en raison d'une restriction de taille de page API. Vous ne devez pas ignorer les backticks dans les blocs de code, uniquement si le bloc de code lui-même est créé à l'aide de backticks et non sur sa propre ligne.

Éditer

J'ai mis fin à la période de prime tôt parce que j'ai demandé à un mod d'obtenir une suspension d'une semaine et je ne voulais pas que la prime soit automatiquement attribuée à la réponse ayant obtenu le score le plus élevé (qui se trouve être la plus longue). Si une nouvelle soumission arrive ou si une soumission est suffisamment lue pour devenir inférieure à 532 octets avant la fin réelle de la période de prime (UTC 00:00 le 1er juin), je vous donnerai une prime pour rester fidèle à ma promesse, après la suspension expire. Si je me souviens bien, je dois doubler la période de prime la prochaine fois, donc si vous obtenez une réponse, vous pourriez obtenir +200 :)

HyperNeutrino
la source
1
Je suis confus par ce qui compte comme un extrait de code valide. Pourquoi pas tout ce qui se trouve dans les balises <code> du html?
Calvin's Hobbies
@HelkaHomba Qu'en est-il des restrictions de nouvelle ligne? Je pourrais essayer de trouver une autre façon de les intégrer.
HyperNeutrino
@HelkaHomba Essentiellement, si la réponse contient du code délimité par des points dans une ligne, il doit être ignoré.
HyperNeutrino
C'est l'une de ces réponses, où il est plus facile de faire la partie principale de la question. Télécharger la page et extraire les blocs de code est plus difficile que de faire la distance levenshtein.
Bálint
1
Cool. Je vérifie.
Matt

Réponses:

1

PowerShell, 532 octets

$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]

J'ai laissé des sauts de ligne là-dedans pour une certaine lisibilité. Les sont toujours reflétés dans mon nombre d'octets.

Je suis presque sûr d'avoir une idée de ça. La partie difficile pour moi était en fait d'obtenir la distance Levenshtein car PowerShell n'a pas de fonction intégrée pour autant que je sache. Pour cette raison, j'ai pu répondre au défi connexe sur la distance de Levenshtein . Lorsque mon code fait référence à une fonction anonyme pour LD, vous pouvez vous référer à cette réponse pour une explication plus détaillée de la façon dont cela fonctionne.

Code avec commentaires et indicateur de progression

Le code peut devenir très lent (à cause du LD), j'ai donc intégré moi-même certains indicateurs de progression afin de pouvoir suivre l'action telle qu'elle se déroulait et ne pas supposer qu'elle était coincée quelque part dans une boucle. Le code de surveillance de la progression n'est pas dans le bloc supérieur ni compté dans mon nombre d'octets.

# 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]

Ma logique pour trouver les blocs de code est de prendre la réponse en HTML et de rechercher un jeu de balises de code, éventuellement entouré d'un jeu de balises pré qui commence sur sa propre ligne. Lors des tests, il a trouvé toutes les données correctes sur 6 ensembles de questions différents.

J'ai essayé de travailler à partir du code de démarque, mais il était trop difficile de trouver le bon bloc de code.

Exemples de parcours

Challenge-Similarity-Detector 97752 122740
57

Challenge-Similarity-Detector 115715 116616
23
Mat
la source
J'ai passé la majeure partie de 3 jours à examiner cela. Ce défi est dans mon top 5 pour les tentatives les plus amusantes. TFTC (Merci pour le défi)
Matt
Bon travail! Merci, je suis content que ça vous ait plu! :)
HyperNeutrino
Remarque: J'ai accordé la prime plus tôt que prévu car je demande une suspension, je ne peux donc pas l'attribuer plus tard. Bon travail! :)
HyperNeutrino
Demander une suspension?
Matt
Oui, j'ai demandé à Dennis de me donner une suspension d'une semaine pour que je puisse me concentrer sur les devoirs. Cela a déjà été fait (même si je suis toujours là ... je ne sais pas quand je vais disparaître).
HyperNeutrino
3

Java + Jsoup, 1027 octets

Les deux premiers arguments sont les ID de question.

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()];}}

Lisible:

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
la source
battez-moi !!!! Agréable!
tuskiomi
1
Bienvenue chez PPCG! Il n'est pas contraire aux règles d'utiliser une bibliothèque tierce, mais nous exigeons que l'utilisation de la bibliothèque soit notée avec le langage (donc une réponse Java qui utilise une bibliothèque appelée JavaHTML serait étiquetée "Java + JavaHTML").
Mego
OK merci! Je garderai cela à l'esprit pour la prochaine fois!
Tomahawk2001913
Rien ne vous empêche d'utiliser une bibliothèque dans ce défi si vous le souhaitez.
Matt
Je devrais peut-être le faire maintenant que quelqu'un a dépassé ma réponse!
Tomahawk2001913
0

Mathematica, 540 octets

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])&


contribution

["115715", "116616"]

production

23

utilise EditDistance intégré qui "donne la distance d'édition ou de Levenshtein entre les chaînes ou les vecteurs u et v."

Quant au cas de test mathématique

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

renvoie 23

Je suppose que je peux jouer au golf un peu plus
Prend quelques minutes pour courir

J42161217
la source