【干货】造数据自动机

· · 科技·工程

前言

使用 Testlib 造数据门槛较高,cyaron 虽然比较方便,但仍然需要预先编译和手动打包。

为了造数据方便,笔者使用 Python 开发了一个自动造数据的轻量级网页,只需要输入 std 和造数据代码,就可以自动打包生成数据 Zip 文件。

环境

pip install flask tqdm

服务器

import os
import zipfile

import tqdm
from flask import Flask, render_template, request, send_file

from lib import global_vars

app = Flask(__name__)

GCC_PATH = 'g++.exe' # 本机 G++ 地址

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'GET':
        return render_template('index.html', error=None)
    print('开始生成数据', flush=True)
    std = request.form.get('std')
    data = request.form.get('data')
    num = int(request.form.get('num'))
    if num <= 0:
        return render_template('index.html', error='数据组数必须为正整数')
    cwd = os.getcwd()
    if 'data' not in cwd:
        os.chdir('./data')
    with open('std.cpp', 'w', encoding='utf-8') as f:
        f.write(std)
    print('开始编译 C++ 程序', flush=True)
    os.system(f'{GCC_PATH} -O2 --static -Wl,--stack=204800000 -o std.exe std.cpp')
    print('编译完成', flush=True)
    for i in tqdm.trange(1, num + 1):
        var = global_vars.copy()
        var['num'] = i
        try:
            exec(data, var)
        except (Exception, SystemExit) as err:
            return render_template('index.html', error=str(err))
        os.system(f'std.exe < {i}.in > {i}.out')
    print('\n数据生成完毕,开始打包 Zip 文件')
    with zipfile.ZipFile('data.zip', 'w', zipfile.ZIP_DEFLATED) as zf:
        for i in tqdm.trange(1, num + 1):
            zf.write(f'{i}.in')
            zf.write(f'{i}.out')
    print('\nZip 文件打包完成')
    os.chdir(cwd)
    return send_file('data/data.zip', as_attachment=True)

@app.route('/help/')
def help_info():
    return render_template('help.html')

if __name__ == '__main__':
    app.run(port=7075, debug=True)

lib.py 如下,可以根据需要自行添加功能:

import random
import math
import string

# import cyaron

global_vars = {
    'random': random.random,
    'randint': random.randint,
    'uniform': random.uniform,
    'choice': random.choice,
    'sample': random.sample,
    'seed': random.seed,
    'randrange': random.randrange,
    'Random': random,
    'Math': math,
    'PI': math.pi,
    'E': math.e,
    'ASCII_LOWERCASE': string.ascii_lowercase,
    'ASCII_UPPERCASE': string.ascii_uppercase,
    'DIGITS': string.digits,
    'ASCII_LETTERS': string.ascii_letters,
    'SENTENCE_SEPARATORS': ',,,,,,,;;:',
    'SENTENCE_TERMINATORS': '....!',
    # 'cyaron': cyaron,
}   # 这个字典存的变量是造数据代码能够使用的

index.html 位于 templates 文件夹下,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自动造数据</title>
</head>
<body>

<form action="/" method="post">
    <label>程序 Std(C++14):</label>
    <br>
    <textarea rows="18" cols="100" id="std" name="std"></textarea>
    <hr>

    <label>
        数据生成器(Python3)
        <a href="/help">查看帮助说明?</a>
    </label>
    <br>
    <textarea rows="18" cols="100" id="data" name="data">with open(f'{num}.in', 'w', encoding='utf-8') as f:
    </textarea>
    <br>
    <label> 数据组数: </label>
    <input type="number" value="10" id="num" name="num">
    <br>
    <input type="submit" value="提交">
</form>
{% if cinfo %}
    <p><strong>编译信息: </strong> {{ cinfo }}
{% endif %}
    <br>
{% if error %}
    <p><strong>错误: </strong> {{ error }}
{% endif %}
</body>
</html>

使用说明

运行 app.py 后,打开 http://127.0.0.1:7075/,如图:

粘贴 std 和数据生成代码,例如:

点击“提交”按钮即可。等待一会,就可以自动下载 data.zip 文件。

快捷安装及使用

在本题的附件中下载 Zip 文件解压即可。

python -m SpnDataMaker

即可使用。若需要修改 G++ 地址,找到相关库的源代码修改即可。

使用示例

这里以 [SPN D-Struct 01] Iridescent 为例。

造数据代码如下:

with open(f'{num}.in', 'w', encoding='utf-8') as f:
    n,q=randint(180000,200000),randint(180000,200000)
    print(n,q,file=f)
    for i in range(n):
        print(randint(-(10**9),10**9),end=' ',file=f)
    print(file=f)
    cnt3 = 0
    for i in range(q):
        if cnt3 >= 60:
            opt = choice([1,2,3,4,5,6])
        else:
            opt = choice([1,2,4,5,6])
        if opt == 3:
            cnt3 += 1
        l,r=randint(1,n),randint(1,n)
        if l>r:
            l,r=r,l
        if opt == 2 and num > 3:
            r=l+randint(1,3)   # 此处卡掉odt
        if opt <= 2:
            print(opt,l,r,randint(-(10**9),10**9),file=f)
        else:
            print(opt,l,r,file=f)