Envelopper (partie de) la bibliothèque AtCoder en Cython pour une utilisation en Python

introduction

Cet article provient de AtCoder Library, DSU Et Fenwick Tree sera rendu disponible depuis Python en utilisant Cython. Je n'ai pas confirmé s'il existe autre chose que DSU et Fenwick Tree qui peut être utilisé de la même manière, mais par exemple, [Segtree](https://atcoder.github.io/ac-library/production/document_ja/segtree. Je pense que c'est difficile car html) utilise des modèles non typés et Cython ne prend pas en charge les modèles non typés (probablement).

La bibliothèque AtCoder est écrite en C ++, c'est donc plus rapide que d'implémenter la même chose en Python. Cependant, comme je l'expliquerai plus tard, le résultat n'est pas rapide par rapport à PyPy.

Cet article aborde rarement la grammaire de Cython, donc si vous êtes intéressé par Cython dans cet article, veuillez également vous référer à d'autres articles. (Cython fonctionne même si vous écrivez le code Python tel quel.)

Qu'est-ce que Cython

C'est un langage pour créer des modules d'extension Python en utilisant C / C ++, et peut appeler des classes et des fonctions C / C ++. C'est un langage pour faire exactement cela. (vraiment?)

Environnement

Cython et la bibliothèque AtCoder sont nécessaires, nous allons donc les installer. Notez que les utilisateurs de Windows peuvent être bloqués dans la compilation de Cython, nous vous recommandons donc d'utiliser Linux ou WSL si possible.

Installer Cython

terminal


$ pip3 install cython

Je vais le faire à. Voyons si Cython peut être utilisé.

hello.pyx


print("Hello, World!")

Un programme qui génère Hello, World!. Notez que l'extension est «.pyx». Compilons et exécutons ceci.

terminal


$ cythonize -i -3 hello.pyx
$ python3 -c "import hello"

Je compile avec la commande cythonize sur la première ligne. L'option -i le convertit d'abord en code C et le compile dans un format qui peut être importé par Python ( .so sous Linux, .pyd sous Windows). L'option -3 est utilisée lorsque Python est une série 3.

Comme dans la deuxième ligne, si vous exécutez le code qui importe hello dans Python et Hello, World! Is en sortie, la construction de l'environnement Cython réussit. AtCoder Library Créez un dossier / opt / ac-library /. (Cela correspond à l'environnement de juge d'AtCoder.) Copiez le dossier atcoder dans https://img.atcoder.jp/practice2/ac-library.zip directement en dessous.

Enveloppez la bibliothèque AtCoder avec Cython

DSU Tout d'abord, activez DSU. Afin d'utiliser la bibliothèque AtCoder de Python, Cython nécessite les deux étapes suivantes:

  1. Rendre la bibliothèque AtCoder disponible à partir de Cython
  2. Enveloppez-le pour une utilisation à partir de Python

1. Rendre la bibliothèque AtCoder disponible à partir de Cython

# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
from libcpp cimport bool
from libcpp.vector cimport vector
cdef extern from "<atcoder/dsu>" namespace "atcoder":
    cdef cppclass dsu:
        dsu(int n) #constructeur
        int merge(int a, int b) #Côté(a, b)Ajouter
        bool same(int a, int b) #Apex a,Si b est concaténé
        int leader(int a) #Élément représentatif du composant de connexion auquel appartient le sommet a
        int size(int a) #La taille du composant de connexion auquel appartient le sommet a
        vector[vector[int]] groups() #Liste de "liste des numéros de sommets d'un composant concaténé"

C'est le code qui a rendu DSU disponible en Cython. La première ligne spécifie que C ++ est utilisé et la deuxième ligne spécifie l'emplacement de la bibliothèque AtCoder. Les lignes 3 et 4 sont importées pour tirer parti des valeurs booléennes et vectorielles C ++. À partir de la 5ème ligne, les méthodes de la classe dsu dans l'espace de noms atcoder de atcoder / dsu.hpp (honnêtement, je ne suis pas sûr de C ++, donc je ne suis pas sûr de cela, donc je vais l'implémenter en interne et documentation DSU. Veuillez lire (: //atcoder.github.io/ac-library/production/document_ja/dsu.html) …….), Qui est utilisé en Cython. Vous avez maintenant un DSU en Cython

cdef dsu *u = new dsu(3)
u.merge(0, 1)
print(u.same(0, 1)) # True
print(u.same(0, 2)) # False

Il est maintenant disponible en tant que.

2. Enveloppez-le pour une utilisation à partir de Python

cdef class DSU:
    cdef dsu *_thisptr
    #constructeur
    def __cinit__(self, int n):
        self._thisptr = new dsu(n)
    #Côté(a, b)Ajouter
    cpdef int merge(self, int a, int b):
        return self._thisptr.merge(a, b)
    #Apex a,Si b est concaténé
    cpdef bool same(self, int a, int b):
        return self._thisptr.same(a, b)
    #Élément représentatif du composant de connexion auquel appartient le sommet a
    cpdef int leader(self, int a):
        return self._thisptr.leader(a)
    #La taille du composant de connexion auquel appartient le sommet a
    cpdef int size(self, int a):
        return self._thisptr.size(a)
    #Liste de "liste des numéros de sommets d'un composant concaténé"
    cpdef vector[vector[int]] groups(self):
        return self._thisptr.groups()

Les fonctions et méthodes définies dans cpdef peuvent également être appelées depuis Python. En créant une classe DSU et en appelant la méthode de la classe dsu en interne, cela signifie que l'utilisation de DSU à partir de Python est réalisée.

Ceci termine le code Cython.

dsu.pyx


# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
from libcpp cimport bool
from libcpp.vector cimport vector
cdef extern from "<atcoder/dsu>" namespace "atcoder":
    cdef cppclass dsu:
        dsu(int n)
        int merge(int a, int b)
        bool same(int a, int b)
        int leader(int a)
        int size(int a)
        vector[vector[int]] groups()
cdef class DSU:
    cdef dsu *_thisptr
    def __cinit__(self, int n):
        self._thisptr = new dsu(n)
    cpdef int merge(self, int a, int b):
        return self._thisptr.merge(a, b)
    cpdef bool same(self, int a, int b):
        return self._thisptr.same(a, b)
    cpdef int leader(self, int a):
        return self._thisptr.leader(a)
    cpdef int size(self, int a):
        return self._thisptr.size(a)
    cpdef vector[vector[int]] groups(self):
        return self._thisptr.groups()

Compilons-le pour qu'il puisse être importé en Python.

terminal


$ cythonize -i -3 dsu.pyx

Exécuter en Python

Écrivons le code pour résoudre DSU Exercise en Python.

ALPC-A.py


from dsu import DSU
n, q = map(int, input().split())
dsu = DSU(n)
res = []
for i in range(q):
    t, u, v = map(int, input().split())
    if t == 0:
        dsu.merge(u, v)
    else:
        if dsu.same(u, v):
            res.append(1)
        else:
            res.append(0)
print('\n'.join(map(str, res)))

Si vous essayez de saisir un échantillon de l'exercice et d'obtenir le résultat correct, c'est un succès pour le moment.

Soumission

Je voudrais en fait le soumettre à DSU Exercises et confirmer que ce sera AC, mais du côté du juge du Coder dsu. Il n'y a pas de pyx ou de module qui le compile, donc si vous le soumettez tel quel, ce sera RE. Vous devez donc intégrer dsu.pyx dans votre code de soumission.

ALPC-A.py


cycode = r"""
# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
from libcpp cimport bool
from libcpp.vector cimport vector
cdef extern from "<atcoder/dsu>" namespace "atcoder":
    cdef cppclass dsu:
        dsu(int n)
        int merge(int a, int b)
        bool same(int a, int b)
        int leader(int a)
        int size(int a)
        vector[vector[int]] groups()
cdef class DSU:
    cdef dsu *_thisptr
    def __cinit__(self, int n):
        self._thisptr = new dsu(n)
    cpdef int merge(self, int a, int b):
        return self._thisptr.merge(a, b)
    cpdef bool same(self, int a, int b):
        return self._thisptr.same(a, b)
    cpdef int leader(self, int a):
        return self._thisptr.leader(a)
    cpdef int size(self, int a):
        return self._thisptr.size(a)
    cpdef vector[vector[int]] groups(self):
        return self._thisptr.groups()
"""
import os, sys
if sys.argv[-1] == 'ONLINE_JUDGE':
    open('dsu.pyx', 'w').write(cycode)
    os.system('cythonize -i -3 dsu.pyx')
from dsu import DSU
n, q = map(int, input().split())
dsu = DSU(n)
res = []
for i in range(q):
    t, u, v = map(int, input().split())
    if t == 0:
        dsu.merge(u, v)
    else:
        if dsu.same(u, v):
            res.append(1)
        else:
            res.append(0)
print('\n'.join(map(str, res)))

Le Python d'AtCoder est en phase de compilation pour l'AOT de Numba (lisez cet article pour plus d'informations). N'est pas inclus dans le temps d'exécution), et ONLINE_JUDGE est donné comme argument de ligne de commande pendant la phase de compilation. En utilisant ceci, if sys.argv [-1] == 'ONLINE_JUDGE': sur la 30ème ligne détermine s'il est en phase de compilation, et s'il est en phase de compilation, il écrit le fichier dans dsu.pyx. , Commande Cythonize à compiler. J'ai [soumis] ce code (https://atcoder.jp/contests/practice2/submissions/17535199) et il est devenu AC en toute sécurité en 424 ms. À titre de comparaison, les volontaires ont DSU dans la bibliothèque AtCoder Python Port. Lors de l'utilisation de python / blob / master / atcoder / dsu.py), lorsque Soumettre en Python, 635 ms, [Soumettre dans PyPy] ](Https://atcoder.jp/contests/practice2/submissions/17535461) C'était 551 ms. ~~ Eh bien, malgré le travail acharné, ça ne va pas aussi vite ~~ Fenwick Tree De même, rendez Fenwick Tree disponible en Python.

fenwick_tree.pyx


# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
cdef extern from "<atcoder/fenwicktree>" namespace "atcoder":
    cdef cppclass fenwick_tree[T]:
        fenwick_tree(int n)
        void add(int p, T x)
        T sum(int l, int r)
ctypedef long long ll
cdef class FenwickTree:
    cdef fenwick_tree[ll] *_thisptr
    def __cinit__(self, int n):
        self._thisptr = new fenwick_tree[ll](n)
    cpdef void add(self, int p, ll x):
        self._thisptr.add(p, x)
    cpdef ll sum(self, int l, int r):
        return self._thisptr.sum(l, r)

C'est fondamentalement le même que DSU, mais Fenwick Tree utilise des modèles. D'après Document, int / uint (unsigned int) / ll (long long) / ull (unsigned long long) Il semble que / modint puisse être spécifié comme type. Ici, le type est ll. (Parce que je pense que int et uint peuvent être remplacés par ll) Si vous voulez un ull Fenwick Tree, réécrivez-le ou créez une autre classe. ~~ modint a abandonné ... ~~

Résolvons les Exercices sur l'arbre Fenwick.

ALPC-B.py


cycode = r"""
# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
cdef extern from "<atcoder/fenwicktree>" namespace "atcoder":
    cdef cppclass fenwick_tree[T]:
        fenwick_tree(int n)
        void add(int p, T x)
        T sum(int l, int r)
ctypedef long long ll
cdef class FenwickTree:
    cdef fenwick_tree[ll] *_thisptr
    def __cinit__(self, int n):
        self._thisptr = new fenwick_tree[ll](n)
    cpdef void add(self, int p, ll x):
        self._thisptr.add(p, x)
    cpdef ll sum(self, int l, int r):
        return self._thisptr.sum(l, r)
"""
import os, sys
if sys.argv[-1] == 'ONLINE_JUDGE':
    open('fenwick_tree.pyx', 'w').write(cycode)
    os.system('cythonize -i -3 fenwick_tree.pyx')
from fenwick_tree import FenwickTree
n, q = map(int, input().split())
ft = FenwickTree(n)
for i, a in enumerate(map(int, input().split())):
    ft.add(i, a)
res = []
for _ in range(q):
    t, u, v = map(int, input().split())
    if t == 0:
        ft.add(u, v)
    else:
        res.append(ft.sum(u, v))
print('\n'.join(map(str, res)))

J'ai [soumis] ce code (https://atcoder.jp/contests/practice2/submissions/17535413) et il est devenu AC en 1287 ms. À titre de comparaison, lors de l'utilisation de Python Ported Fenwick Tree, Submit in Python //atcoder.jp/contests/practice2/submissions/17535420) à 4663 ms, Soumettre avec PyPy à 1001 ms fait. ~~ Plus lent que PyPy! Je suis vif ... ~~

Comparaison

DSU

Cython + Python Python PyPy
424 ms 635 ms 551 ms

Fenwick Tree

Cython + Python Python PyPy
1287 ms 4663 ms 1001 ms

prime

En fait, si vous écrivez la partie qui a été faite en Python en Cython, ce sera beaucoup plus rapide que PyPy. Ce qui suit est le code lorsque Fenwick Tree Exercises est entièrement écrit en Cython.

ALPC-B.pyx


# distutils: language=c++
# distutils: include_dirs=/opt/ac-library
from libc.stdio cimport scanf, printf
from libcpp.vector cimport vector
cdef extern from "<atcoder/fenwicktree>" namespace "atcoder":
    cdef cppclass fenwick_tree[T]:
        fenwick_tree(int n)
        void add(int p, T x)
        T sum(int l, int r)
ctypedef long long ll
cdef int n, q, i, t
cdef ll u, v, a
scanf('%d%d', &n, &q)
cdef fenwick_tree[ll] *ft = new fenwick_tree[ll](n)
for i in range(n):
    scanf('%lld', &a)
    ft.add(i, a)
for i in range(q):
    scanf('%d%lld%lld', &t, &u, &v)
    if t == 0:
        ft.add(u, v)
    else:
        printf('%lld\n', ft.sum(u, v))

Cette soumission était de 251 ms.

Cython Cython + Python Python PyPy
251 ms 1287 ms 4663 ms 1001 ms

Super rapide. Alors, pourquoi Cython + Python est-il plus lent que PyPy? Je soupçonnais que la cause était la lenteur du traitement des boucles de Python. À titre expérimental, soumettons du code en Python et PyPy qui accepte simplement l'entrée pour ce problème et voyons le temps d'exécution.

ALPC-B-input.py


n, q = map(int, input().split())
for i, a in enumerate(map(int, input().split())):
    i, a
for _ in range(q):
    t, u, v = map(int, input().split())

Le résultat est de 1012 ms pour Python et de 687 ms pour PyPy. fait. En soustrayant ce résultat du temps d'exécution du code AC, il semble que le temps de traitement de Fenwick Tree prenne environ 275ms pour Cython + Python et 314ms pour PyPy. ~~ Subtil ~~

Sommaire

Je pense que vous devriez utiliser PyPy.

Recommended Posts

Envelopper (partie de) la bibliothèque AtCoder en Cython pour une utilisation en Python
Comment utiliser la bibliothèque C en Python
Envelopper C avec Cython pour une utilisation à partir de Python
Envelopper C ++ avec Cython pour une utilisation à partir de Python
Utilisez l'application LibreOffice en Python (3) Ajouter une bibliothèque
Utilisons les données ouvertes de "Mamebus" en Python
[Internal_math version (2)] Décodage de la bibliothèque AtCoder ~ Implémentation en Python ~
Vérifiez le fonctionnement de Python pour .NET dans chaque environnement
[python] Comment utiliser Matplotlib, une bibliothèque pour dessiner des graphiques
Google recherche la chaîne sur la dernière ligne du fichier en Python
L'histoire de la participation à AtCoder
Utilisez networkx, une bibliothèque qui gère les graphiques en python (Partie 2: Tutoriel)
[Introduction à Python] Comment utiliser l'opérateur in dans l'instruction for?
Vérifiez le comportement du destroyer en Python
Implémenter une partie du processus en C ++
Résumé de diverses instructions for en Python
Le résultat de l'installation de python sur Anaconda
Qu'est-ce que "mahjong" dans la bibliothèque Python? ??
MongoDB avec Python pour la première fois
Principes de base pour exécuter NoxPlayer en Python
Pandas du débutant, par le débutant, pour le débutant [Python]
Fiche de triche AtCoder en python (pour moi-même)
À la recherche du FizzBuzz le plus rapide en Python
Utilisez pathlib dans Maya (Python2.7) en préparation du prochain Python3.7
À propos du code Python pour une moyenne mobile simple en supposant l'utilisation de Numba
Lecture de code de Safe, une bibliothèque pour examiner la force des mots de passe en Python
Obtenez la clé pour la migration de la deuxième couche de données JSON avec python
Sortie du nombre de cœurs de processeur en Python
[Python] Trier la liste de pathlib.Path dans l'ordre naturel
Récupérer l'appelant d'une fonction en Python
Faites correspondre la distribution de chaque groupe en Python
Comment utiliser la bibliothèque d'images Python dans la série python3
Afficher le résultat du traitement de la géométrie en Python
Utilisez Logger avec Python pour le moment
Copiez la liste en Python
Résumé de l'utilisation de MNIST avec Python
Conseils pour accéder à l'API ATND avec Python
Découvrez la fraction de la valeur saisie en python
[Python] Lire le code source de Bottle Part 1
Traitement d'image? L'histoire du démarrage de Python pour
Se débarrasser des images DICOM avec Python Partie 2
Trouvez la solution de l'équation d'ordre n avec python
N'utilisez pas readlines () dans votre instruction Python for!
L'histoire de la lecture des données HSPICE en Python
[Note] À propos du rôle du trait de soulignement "_" en Python
Résolution d'équations de mouvement en Python (odeint)
Sortie sous la forme d'un tableau python
Code pour vérifier le fonctionnement de Python Matplot lib
[Python + OpenCV] Peignez la partie transparente de l'image en blanc
Histoire de base de l'héritage en Python (pour les débutants)
Utilisez autre chose qu'une chaîne <br> pour la clé <br> dict en Python
Je souhaite utiliser Python dans l'environnement de pyenv + pipenv sous Windows 10
Script Python pour obtenir une liste d'exemples d'entrée pour le concours AtCoder
Appuyez sur les exemples v20-python-samples de la bibliothèque d'encapsuleurs d'API REST OANDA v20 pour Python
AtCoder # 36 quotidien avec Python
Daily AtCoder # 32 en Python
Daily AtCoder # 6 en Python
Daily AtCoder # 18 en Python