[PYTHON] Competitive Pro Input Auto Generator und objektorientierte Story

Überblick

<! - * Erzeugt auch automatisch (Python-) Quellcode, um Eingaben zu erhalten->

Diejenigen, die die automatische Generierungsfunktion nutzen möchten, sowie diejenigen, die etwas über die Programmierung wissen möchten, die einen Schritt weiter vom Wettbewerbsprofi entfernt ist Ich denke, dieser Inhalt kann von denen gelesen werden, die sich mit Objektorientierung befassen. Den gesamten Quellcode finden Sie unter [hier] 1.

Was Sie tun können: Generieren Sie automatisch Eingabetestfälle

Wenn Sie ein solches Eingabemakro vorbereiten →

Int(N,2,5)
Float(a,1,100,5) Perm(1,N+4)
Str([a-z]{3,2*N})
*N(2)

Der Eingang wird ausgegeben →

output: 
4
54.81887 3 2 4 7 1 8 5 6
yyl
4.32497 4 1 6 5 3 8 7 2
yziuqiac
42.84603 3 2 4 7 8 6 5 1
vsjajs
65.07176 7 5 8 3 4 6 1 2
rbq

Verschiedene andere Dinge

Gewichtete Grafik:

Int(N,3,5) Int(M,N-1,N*(N-1)//2)
Graph1(N,M,0,1000)

output:
4 5
1 2 392
1 3 328
1 4 891
2 3 264
3 4 227

String:

Int(N,4,6) Int(M,4,6)
Str([.#]{M})
*N(1)

output:
4 5
###.#
#.#.#
#..##
.###.

etc.

Wenn Sie an der Funktion selbst interessiert sind, finden Sie eine Liste in [hier] 1.

<! - ## Was Sie tun können: Generieren Sie automatisch Quellcode, um Eingaben zu erhalten

Anstatt Eingaben zu spucken, können Sie auch die Funktion input () von Python verwenden, um Quellcode zu generieren, der Eingaben empfängt:


``` -->

 * Da wir bisher nur die innere Klasse definiert haben, müssen Sie Python mit dieser Klasse codieren, wenn Sie viele Testfälle gleichzeitig generieren oder in eine Datei schreiben möchten.

## Realisierungsmethode

 Es hat eine objektorientierte Implementierung.
 Grob

 1. Klasse, die für das Laden des gesamten Makros verantwortlich ist (LineCollection)
 1. Klasse (Zeile), die für das zeilenweise Lesen des Makros verantwortlich ist
 1. Die Klasse (Item und seine Unterklassen), die für das Leerzeichen in einer Zeile des Makros verantwortlich ist

 Es ist unterteilt in. Es ist ein Bild, das ich i + 1 verwalte und die notwendige Arbeit (i = 1,2) wirft.
 Allen Klassen sind folgende Methoden gemeinsam:

 1. from_str (): Analysiert die Eingabemakrozeichenfolge und initialisiert die Klasse.

 1. generate (): Erzeugt einen Testfall mit zufälliger Eingabe für den von ihm verwalteten Bereich.

 Zusammenfassend sieht es wie in der folgenden Tabelle aus:

 ||LineCollection|Line|Item|
 |---|---|---|---|
 |from_str()|Laden des gesamten Makros|Lesen Sie eine Zeile|Ein Wort lesen|
 |generate()|Ausgabegenerierung für das gesamte Makro|Ausgabeerzeugung für eine Zeile|Ausgabeerzeugung für ein Wort|

 <!-- |generate()/generate_source()|Ausgabegenerierung für das gesamte Makro|Ausgabeerzeugung für eine Zeile|Ausgabeerzeugung für ein Wort| -->


 Der Punkt ist

 * Um das Ganze zu nutzen, LineCollection.from_str (Eingabemakro) → generate ()

 <! - * Um das Ganze zu verwenden, LineCollection.from_str (Eingabemakro) → generate () / generate_source () ->

 * Wenn Sie die Anzahl neuer Makrotypen erhöhen möchten, definieren Sie einfach eine Unterklasse der Item-Klasse mit den oben genannten drei Methoden (Line oder LineCollection müssen nicht geändert werden).

 Was für ein Ort wie.

## Quellcode

 Insbesondere werden wir den Quellcode überprüfen.

 LineCollection

 ||<font color=green> LineCollection|Line|Item|
 |---|---|---|---|
 |from_str()|<font color=green>Laden des gesamten Makros</font>|Lesen Sie eine Zeile|Ein Wort lesen|
 |generate()|<font color=green>Ausgabegenerierung für das gesamte Makro|Ausgabeerzeugung für eine Zeile|Ausgabeerzeugung für ein Wort|

 <!-- |generate()/generate_source()|<font color=green>Ausgabegenerierung für das gesamte Makro|Ausgabeerzeugung für eine Zeile|Ausgabeerzeugung für ein Wort| -->


 self.ls hat eine Liste von Zeilen für jede Zeile des Makros.
 Es ist etwas kompliziert, Makros wie "* N (1)" zu unterstützen, aber wir tun es

 * from_str () = Gesamtes Makro laden: Initialisieren Sie die Zeile, die jeder Zeile entspricht, und fügen Sie sie in self.ls ein
 * generate () = Ausgabe für das gesamte Makro generieren: Generiert eine Ausgabe für jede Zeile in self.ls und gibt sie mit einem Zeilenvorschubzeichen zurück

 nur.

```python
class LineCollection:
    def __init__(self, ls, s=None):
        """ls: list of Line
        """
        self.ls = ls
        self.s = s
    @classmethod
    def from_str(cls, s):
        lines = s.split("\n")
        i = 0
        ls = []
        for i in range(len(lines)):
            if lines[i].startswith("*"):
                name, num = lines[i][1:].split("(",1)
                num = int(num[:-1])
                ls.append((name, num))
            else:
                l = Line.from_str(lines[i])
                ls.append(l)
        return cls(ls, s)
    def generate(self):
        i = 0
        prv = 0
        output = []
        while i<len(self.ls):
            while i<len(self.ls) and not isinstance(self.ls[i], tuple):
                i += 1
            if i<len(self.ls) and isinstance(self.ls[i], tuple):
                m, num = self.ls[i]
                i += 1
            else:
                m = 0
                num = 0
            for j in range(prv, i-num-1):
                if isinstance(self.ls[j], tuple):
                    continue
                output.append(self.ls[j].generate())
            if num!=0:
                try:
                    m = Item.names[m]
                except KeyError:
                    raise ValueError("Undefinierte Anzahl von Testfällen:Ist die Angabe, wie viele Zeilen oben angezeigt werden sollen, nicht falsch?")
                for _ in range(m):
                    for j in range(i-num-1, i-1):
                        if isinstance(self.ls[j], tuple):
                            continue
                        output.append(self.ls[j].generate())
            prv = i
        return "\n".join(output)

Line

LineCollection Line Item
from_str() Laden des gesamten Makros Lesen Sie eine Zeile Ein Wort lesen
generate() Ausgabegenerierung für das gesamte Makro Ausgabeerzeugung für eine Zeile Ausgabeerzeugung für ein Wort

self.l verwaltet eine Liste von Elementen, die in der angezeigten Zeile vorhanden sind. Ähnlich wie bei der LineCollection,

Ich mache das Wenn wir den Klassennamen lesen und das Element initialisieren, erhalten wir das Klassenobjekt von der in Python integrierten Funktion globals ().

def evaluate_item(ss):
    cc, tmp = ss.split("(", 1)
    vv = tmp[:-1]
    return globals()[cc].from_str(vv)

class Line:
    def __init__(self, l, s=None):
        """
        correspond to a line of input file
        l: list of Item
        """
        self.l = l
        self.s = s
    @classmethod
    def from_str(cls, s):
        l = []
        for ss in s.split():
            l.append(evaluate_item(ss))
        return cls(l)
    def generate(self):
        return " ".join([item.generate() for item in self.l])

Item

LineCollection Line Item
from_str() Laden des gesamten Makros Lesen Sie eine Zeile Ein Wort lesen
generate() Ausgabegenerierung für das gesamte Makro Ausgabeerzeugung für eine Zeile Ausgabeerzeugung für ein Wort

Schließlich Item, das für jedes Makro definiert ist, das Sie unterstützen möchten. In einem solchen Fall ist das Erstellen und Erben einer Basisklasse hinsichtlich Lesbarkeit, Wartbarkeit und Codierungseffizienz überlegen. Wir haben die Item-Klasse als Basisklasse wie folgt vorbereitet:

class Item:
    names = {}
    def __init__(self, s=None, **keys):
        self.s = s
    @classmethod
    def from_str(cls, s):
        pass
    def evaluate(self, s):
        for k in Item.names.keys():
            if k in s:
                s = s.replace(k, str(Item.names[k]))
        return eval(s)
    def generate(self):
        pass
    def __str__(self):
        if hasattr(self, "s"):
            return self.s

Item ist eine Klasse, die nicht so verwendet werden kann, wie sie ist. Es ist daher besser, einen Zauberspruch zu verwenden, um sie darzustellen. Diesmal wird sie jedoch weggelassen. Selbst wenn Sie nur erklären, dass Sie über diese Methoden verfügen, hat dies große Vorteile, z. B. das Verständnis für andere beim Hinzufügen von Funktionen.

Erstellen Sie darunter eine Klasse für jedes Makro, das Sie realisieren möchten. Zum Beispiel Int

class Int(Item):
    def __init__(self, name, low, high, s=None, **keys):
        """
        correspond to the input value between two spaces
        name: str
            name of variable
        low/high : str
            min / max (inclusive)
        """
        self.name = name
        self.low = low
        self.high = high
        self.keys = keys
        Item.__init__(self, s)
    @classmethod
    def from_str(cls, s):
        name, low, high = s.split(",")
        return cls(name, low, high, s=s)
    def generate(self):
        low, high = self.evaluate(self.low), self.evaluate(self.high)
        value = utils.rng.integers(low, high+1)
        Item.names[self.name] = value
        return str(value)

Wenn die Reihenfolge 1 ... N ist

class Perm(Item):
    """permutation of [low, low+1, ..., high-1, high]
    """
    def __init__(self, low, high, s=None):
        self.low = low
        self.high = high
        self.s = s
    @classmethod
    def from_str(cls, s):
        low, high = s.split(",")
        return cls(low, high, s=s)
    def generate(self):
        low, high = self.evaluate(self.low), self.evaluate(self.high)
        return " ".join(map(str, (utils.rng.permutation(high-low+1) + low).tolist()))

Es ist wie geformt. Wenn Zufallszahlen benötigt werden, werden sie in einen extern definierten Zufallsgenerator geworfen:

utils.py


import numpy as np
SEED = 0
rng = np.random.default_rng(SEED)

Bei der Verwendung von Zufallszahlen ist es richtig, die Funktion nicht jedes Mal aufzurufen, sondern Zufallskorrekturen (gegebenenfalls mehrere) vorzubereiten und entsprechend dem Umfang ordnungsgemäß zu verwenden. In diesem Fall hängt es jedoch davon ab, ob es wünschenswert ist, die Zufallszahl festzulegen (um die Reproduzierbarkeit zu erhalten) oder das Ergebnis für jede Ausführung zu ändern, sodass es je nach Anwendungsfall erforderlich sein kann, es neu zu schreiben. ..

Andere Makros werden nacheinander auf die gleiche Weise unterstützt. Wenn es sich beispielsweise um ein Diagramm handelt, wird es mithilfe von Funktionen wie der Erzeugung eines zufälligen Networkx-Diagramms realisiert. Wenn es sich um eine Zeichenfolge handelt, wird dies mithilfe von Funktionen wie der zufälligen Generierung einer Zeichenfolge realisiert, die mit dem Muster des regulären Ausdrucks durch rstr übereinstimmt. Was tun?

Es ist nur eine Frage der Implementierung, sodass der Aufwand für das Hinzufügen von Funktionen reduziert wird!

Zusammenfassung

<! - * Erzeugt auch automatisch (Python-) Quellcode, um Eingaben zu erhalten->

TODO

Recommended Posts

Competitive Pro Input Auto Generator und objektorientierte Story
Competitive Pro mit Python und VSCode-Vereinfachung der Standardeingabe und Automatisierung der Beurteilung von Beispielfällen