CYaRon 文档

· · 个人记录

CYaRon 文档

CYaRon 是一个可以帮助你快速生成随机数据的工具库,目标是实现帮您5分钟内生成一组测试数据。

使用CYaRon

CYaRon 使用 Python 编写。在安装好 Python 的计算机上,下载 CYaRon 并放置在合适的目录下或使用 Python 自带的 pip 安装工具输入命令pip install cyaron即可安装 CYaRon。

CYaRon 的工具包可以帮助你写出 Python 数据生成器。通过使用各种工具包,你可以控制输入/输出文件,并将std程序(标程)的输出写入到输出文件中。

要了解 CYaRon 的使用,可以查看本项目的examples

若您对 Python 不熟悉,可看快速入门教程。

要详细了解 CYaRon 的各部分,可以查看右侧侧边栏中的各文档页面。

贡献

所有的贡献者请查看光荣榜页面,衷心感谢他们对CYaRon项目的付出。

欢迎您对 CYaRon 做出贡献。若您有希望加入的功能,可以给我们提出 Issue ,或者自己动手实现,然后发起 Pull Request。

有关于如何做出贡献的更详细内容,请查看如何做出贡献。

Python 30分钟入门指南

为什么 OIer 要学 Python?

为什么我们要准备一个30分钟入门指南?

按理说,在一个库中加入一个从零开始学习某语言的指南是挺搞笑的事情,但是为了节省您的学习成本,我们搬运了一个入门指南。

Python2 和 Python3 有些不太兼容的地方,但是 CYaRon 尽可能两者都兼容。

本教程讲述 Python3 。

本指南部分转载自 https://learnxinyminutes.com/docs/python3/

环境安装

Windows

访问 https://www.python.org/downloads/ ,下载并安装。

使用交互式界面,在开始菜单打开Python3 IDLE即可,您可以开始以下的教程代码。

您也可以新建一个filename.py的文件,写上Python脚本,然后在命令行中输入python filename.py运行。您还可以用IDLE打开这个文件,按F5运行。

您可以在命令提示符中输入pip install cyaron,安装 CYaRon 。

如果要更新 CYaRon,您可以输入pip install --upgrade cyaron

您也可以在 GitHub 中下载或者克隆。不过这一部分就没打算展开讲了。

macOS、Linux

macOS 以及绝大部分 Linux 发行版自带 Python,无需特别安装。

您可以在终端中输入pip install cyaron,安装 CYaRon 。

如果要更新 CYaRon,您可以输入pip --upgrade

入门指南正文

# 用井字符开头的是单行注释

""" 多行字符串用三个引号
    包裹,也常被用来做多
    行注释
"""

####################################################
## 1. 原始数据类型和运算符
####################################################

# 整数
3  # => 3

# 算术没有什么出乎意料的
1 + 1  # => 2
8 - 1  # => 7
10 * 2  # => 20

# 但是除法例外,会自动转换成浮点数
35 / 5  # => 7.0
5 / 3  # => 1.6666666666666667

# 整数除法的结果都是向下取整
5 // 3     # => 1
5.0 // 3.0 # => 1.0 # 浮点数也可以
-5 // 3  # => -2
-5.0 // 3.0 # => -2.0

# 浮点数的运算结果也是浮点数
3 * 2.0 # => 6.0

# 模除
7 % 3 # => 1

# x的y次方
2**4 # => 16

# 用括号决定优先级
(1 + 3) * 2  # => 8

# 布尔值
True
False

# 用not取非
not True  # => False
not False  # => True

# 逻辑运算符,注意and和or都是小写
True and False #=> False
False or True #=> True

# 整数也可以当作布尔值
0 and 2 #=> 0
-5 or 0 #=> -5
0 == False #=> True
2 == True #=> False
1 == True #=> True

# 用==判断相等
1 == 1  # => True
2 == 1  # => False

# 用!=判断不等
1 != 1  # => False
2 != 1  # => True

# 比较大小
1 < 10  # => True
1 > 10  # => False
2 <= 2  # => True
2 >= 2  # => True

# 大小比较可以连起来!
1 < 2 < 3  # => True
2 < 3 < 2  # => False

# 字符串用单引双引都可以
"这是个字符串"
'这也是个字符串'

# 用加号连接字符串
"Hello " + "world!"  # => "Hello world!"

# 字符串可以被当作字符列表
"This is a string"[0]  # => 'T'

# 用.format来格式化字符串
"{} can be {}".format("strings", "interpolated")

# 可以重复参数以节省时间
"{0} be nimble, {0} be quick, {0} jump over the {1}".format("Jack", "candle stick")
#=> "Jack be nimble, Jack be quick, Jack jump over the candle stick"

# 如果不想数参数,可以用关键字
"{name} wants to eat {food}".format(name="Bob", food="lasagna") #=> "Bob wants to eat lasagna"

# 如果你的Python3程序也要在Python2.5以下环境运行,也可以用老式的格式化语法
"%s can be %s the %s way" % ("strings", "interpolated", "old")

# None是一个对象
None  # => None

# 当与None进行比较时不要用 ==,要用is。is是用来比较两个变量是否指向同一个对象。
"etc" is None  # => False
None is None  # => True

# None,0,空字符串,空列表,空字典都算是False
# 所有其他值都是True
bool(0)  # => False
bool("")  # => False
bool([]) #=> False
bool({}) #=> False

####################################################
## 2. 变量和集合
####################################################

# print是内置的打印函数
print("I'm Python. Nice to meet you!")

# 在给变量赋值前不用提前声明
# 传统的变量命名是小写,用下划线分隔单词
some_var = 5
some_var  # => 5

# 访问未赋值的变量会抛出异常
# 参考流程控制一段来学习异常处理
some_unknown_var  # 抛出NameError

# 用列表(list)储存序列
li = []
# 创建列表时也可以同时赋给元素
other_li = [4, 5, 6]

# 用append在列表最后追加元素
li.append(1)    # li现在是[1]
li.append(2)    # li现在是[1, 2]
li.append(4)    # li现在是[1, 2, 4]
li.append(3)    # li现在是[1, 2, 4, 3]
# 用pop从列表尾部删除
li.pop()        # => 3 且li现在是[1, 2, 4]
# 把3再放回去
li.append(3)    # li变回[1, 2, 4, 3]

# 列表存取跟数组一样
li[0]  # => 1
# 取出最后一个元素
li[-1]  # => 3

# 越界存取会造成IndexError
li[4]  # 抛出IndexError

# 列表有切割语法
li[1:3]  # => [2, 4]
# 取尾
li[2:]  # => [4, 3]
# 取头
li[:3]  # => [1, 2, 4]
# 隔一个取一个
li[::2]   # =>[1, 4]
# 倒排列表
li[::-1]   # => [3, 4, 2, 1]
# 可以用三个参数的任何组合来构建切割
# li[始:终:步伐]

# 用del删除任何一个元素
del li[2]   # li is now [1, 2, 3]

# 列表可以相加
# 注意:li和other_li的值都不变
li + other_li   # => [1, 2, 3, 4, 5, 6]

# 用extend拼接列表
li.extend(other_li)   # li现在是[1, 2, 3, 4, 5, 6]

# 用in测试列表是否包含值
1 in li   # => True

# 用len取列表长度
len(li)   # => 6

# 元组是不可改变的序列
tup = (1, 2, 3)
tup[0]   # => 1
tup[0] = 3  # 抛出TypeError

# 列表允许的操作元组大都可以
len(tup)   # => 3
tup + (4, 5, 6)   # => (1, 2, 3, 4, 5, 6)
tup[:2]   # => (1, 2)
2 in tup   # => True

# 可以把元组合列表解包,赋值给变量
a, b, c = (1, 2, 3)     # 现在a是1,b是2,c是3
# 元组周围的括号是可以省略的
d, e, f = 4, 5, 6
# 交换两个变量的值就这么简单
e, d = d, e     # 现在d是5,e是4

# 用字典表达映射关系
empty_dict = {}
# 初始化的字典
filled_dict = {"one": 1, "two": 2, "three": 3}

# 用[]取值
filled_dict["one"]   # => 1

# 用keys获得所有的键。因为keys返回一个可迭代对象,所以在这里把结果包在list里。我们下面会详细介绍可迭代。
# 注意:字典键的顺序是不定的,你得到的结果可能和以下不同。
list(filled_dict.keys())   # => ["three", "two", "one"]

# 用values获得所有的值。跟keys一样,要用list包起来,顺序也可能不同。
list(filled_dict.values())   # => [3, 2, 1]

# 用in测试一个字典是否包含一个键
"one" in filled_dict   # => True
1 in filled_dict   # => False

# 访问不存在的键会导致KeyError
filled_dict["four"]   # KeyError

# 用get来避免KeyError
filled_dict.get("one")   # => 1
filled_dict.get("four")   # => None
# 当键不存在的时候get方法可以返回默认值
filled_dict.get("one", 4)   # => 1
filled_dict.get("four", 4)   # => 4

# setdefault方法只有当键不存在的时候插入新值
filled_dict.setdefault("five", 5)  # filled_dict["five"]设为5
filled_dict.setdefault("five", 6)  # filled_dict["five"]还是5

# 字典赋值
filled_dict.update({"four":4}) #=> {"one": 1, "two": 2, "three": 3, "four": 4}
filled_dict["four"] = 4  # 另一种赋值方法

# 用del删除
del filled_dict["one"]  # 从filled_dict中把one删除

# 用set表达集合
empty_set = set()
# 初始化一个集合,语法跟字典相似。
some_set = {1, 1, 2, 2, 3, 4}   # some_set现在是{1, 2, 3, 4}

# 可以把集合赋值于变量
filled_set = some_set

# 为集合添加元素
filled_set.add(5)   # filled_set现在是{1, 2, 3, 4, 5}

# & 取交集
other_set = {3, 4, 5, 6}
filled_set & other_set   # => {3, 4, 5}

# | 取并集
filled_set | other_set   # => {1, 2, 3, 4, 5, 6}

# - 取补集
{1, 2, 3, 4} - {2, 3, 5}   # => {1, 4}

# in 测试集合是否包含元素
2 in filled_set   # => True
10 in filled_set   # => False

####################################################
## 3. 流程控制和迭代器
####################################################

# 先随便定义一个变量
some_var = 5

# 这是个if语句。注意缩进在Python里是有意义的
# 印出"some_var比10小"
if some_var > 10:
    print("some_var比10大")
elif some_var < 10:    # elif句是可选的
    print("some_var比10小")
else:                  # else也是可选的
    print("some_var就是10")

"""
用for循环语句遍历列表
打印:
    dog is a mammal
    cat is a mammal
    mouse is a mammal
"""
for animal in ["dog", "cat", "mouse"]:
    print("{} is a mammal".format(animal))

"""
"range(number)"返回数字列表从0到给的数字
打印:
    0
    1
    2
    3
"""
for i in range(4):
    print(i)

"""
while循环直到条件不满足
打印:
    0
    1
    2
    3
"""
x = 0
while x < 4:
    print(x)
    x += 1  # x = x + 1 的简写

# 用try/except块处理异常状况
try:
    # 用raise抛出异常
    raise IndexError("This is an index error")
except IndexError as e:
    pass    # pass是无操作,但是应该在这里处理错误
except (TypeError, NameError):
    pass    # 可以同时处理不同类的错误
else:   # else语句是可选的,必须在所有的except之后
    print("All good!")   # 只有当try运行完没有错误的时候这句才会运行

# Python提供一个叫做可迭代(iterable)的基本抽象。一个可迭代对象是可以被当作序列
# 的对象。比如说上面range返回的对象就是可迭代的。

filled_dict = {"one": 1, "two": 2, "three": 3}
our_iterable = filled_dict.keys()
print(our_iterable) # => range(1,10) 是一个实现可迭代接口的对象

# 可迭代对象可以遍历
for i in our_iterable:
    print(i)    # 打印 one, two, three

# 但是不可以随机访问
our_iterable[1]  # 抛出TypeError

# 可迭代对象知道怎么生成迭代器
our_iterator = iter(our_iterable)

# 迭代器是一个可以记住遍历的位置的对象
# 用__next__可以取得下一个元素
our_iterator.__next__()  #=> "one"

# 再一次调取__next__时会记得位置
our_iterator.__next__()  #=> "two"
our_iterator.__next__()  #=> "three"

# 当迭代器所有元素都取出后,会抛出StopIteration
our_iterator.__next__() # 抛出StopIteration

# 可以用list一次取出迭代器所有的元素
list(filled_dict.keys())  #=> Returns ["one", "two", "three"]

####################################################
## 4. 函数
####################################################

# 用def定义新函数
def add(x, y):
    print("x is {} and y is {}".format(x, y))
    return x + y    # 用return语句返回

# 调用函数
add(5, 6)   # => 印出"x is 5 and y is 6"并且返回11

# 也可以用关键字参数来调用函数
add(y=6, x=5)   # 关键字参数可以用任何顺序

# 我们可以定义一个可变参数函数
def varargs(*args):
    return args

varargs(1, 2, 3)   # => (1, 2, 3)

# 我们也可以定义一个关键字可变参数函数
def keyword_args(**kwargs):
    return kwargs

# 我们来看看结果是什么:
keyword_args(big="foot", loch="ness")   # => {"big": "foot", "loch": "ness"}

# 这两种可变参数可以混着用
def all_the_args(*args, **kwargs):
    print(args)
    print(kwargs)
"""
all_the_args(1, 2, a=3, b=4) prints:
    (1, 2)
    {"a": 3, "b": 4}
"""

# 调用可变参数函数时可以做跟上面相反的,用*展开序列,用**展开字典。
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args)   # 相当于 foo(1, 2, 3, 4)
all_the_args(**kwargs)   # 相当于 foo(a=3, b=4)
all_the_args(*args, **kwargs)   # 相当于 foo(1, 2, 3, 4, a=3, b=4)

# 函数作用域
x = 5

def setX(num):
    # 局部作用域的x和全局域的x是不同的
    x = num # => 43
    print (x) # => 43

def setGlobalX(num):
    global x
    print (x) # => 5
    x = num # 现在全局域的x被赋值
    print (x) # => 6

setX(43)
setGlobalX(6)

# 函数在Python是一等公民
def create_adder(x):
    def adder(y):
        return x + y
    return adder

add_10 = create_adder(10)
add_10(3)   # => 13

# 也有匿名函数
(lambda x: x > 2)(3)   # => True

# 内置的高阶函数
map(add_10, [1, 2, 3])   # => [11, 12, 13]
filter(lambda x: x > 5, [3, 4, 5, 6, 7])   # => [6, 7]

# 用列表推导式可以简化映射和过滤。列表推导式的返回值是另一个列表。
[add_10(i) for i in [1, 2, 3]]  # => [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5]   # => [6, 7]

####################################################
## 5. 类
####################################################

# 定义一个继承object的类
class Human(object):

    # 类属性,被所有此类的实例共用。
    species = "H. sapiens"

    # 构造方法,当实例被初始化时被调用。注意名字前后的双下划线,这是表明这个属
    # 性或方法对Python有特殊意义,但是允许用户自行定义。你自己取名时不应该用这
    # 种格式。
    def __init__(self, name):
        # Assign the argument to the instance's name attribute
        self.name = name

    # 实例方法,第一个参数总是self,就是这个实例对象
    def say(self, msg):
        return "{name}: {message}".format(name=self.name, message=msg)

    # 类方法,被所有此类的实例共用。第一个参数是这个类对象。
    @classmethod
    def get_species(cls):
        return cls.species

    # 静态方法。调用时没有实例或类的绑定。
    @staticmethod
    def grunt():
        return "*grunt*"

# 构造一个实例
i = Human(name="Ian")
print(i.say("hi"))     # 印出 "Ian: hi"

j = Human("Joel")
print(j.say("hello"))  # 印出 "Joel: hello"

# 调用一个类方法
i.get_species()   # => "H. sapiens"

# 改一个共用的类属性
Human.species = "H. neanderthalensis"
i.get_species()   # => "H. neanderthalensis"
j.get_species()   # => "H. neanderthalensis"

# 调用静态方法
Human.grunt()   # => "*grunt*"

####################################################
## 6. 模块
####################################################

# 用import导入模块
import math
print(math.sqrt(16))  # => 4.0

# 也可以从模块中导入个别值
from math import ceil, floor
print(ceil(3.7))  # => 4.0
print(floor(3.7))   # => 3.0

# 可以导入一个模块中所有值
# 警告:不建议这么做
from math import *

# 如此缩写模块名字
import math as m
math.sqrt(16) == m.sqrt(16)   # => True

# Python模块其实就是普通的Python文件。你可以自己写,然后导入,
# 模块的名字就是文件的名字。

# 你可以这样列出一个模块里所有的值
import math
dir(math)

####################################################
## 7. 高级用法
####################################################

# 用生成器(generators)方便地写惰性运算
def double_numbers(iterable):
    for i in iterable:
        yield i + i

# 生成器只有在需要时才计算下一个值。它们每一次循环只生成一个值,而不是把所有的
# 值全部算好。这意味着double_numbers不会生成大于15的数字。
#
# range的返回值也是一个生成器,不然一个1到900000000的列表会花很多时间和内存。
#
# 如果你想用一个Python的关键字当作变量名,可以加一个下划线来区分。
range_ = range(1, 900000000)
# 当找到一个 >=30 的结果就会停
for i in double_numbers(range_):
    print(i)
    if i >= 30:
        break

基本入门

CYaRon是一个基于Python的测试数据生成库。要使用CYaRon,您首先应当安装Python。

您可以访问https://www.python.org/downloads/获得具体的安装步骤信息。

您如果不太了解Python的使用方法,可以查看快速入门教程。

在您安装完毕后,可以通过pip获取CYaRon:pip install cyaron

如果您不方便使用pip,也可直接下载压缩包,解压缩后在根目录进行脚本编写。

一个典型的数据生成器脚本如下所示:

#!/usr/bin/env python

from cyaron import * # 引入CYaRon的库

_n = ati([0, 7, 50, 1E4]) # ati函数将数组中的每一个元素转换为整形,方便您可以使用1E4一类的数来表示数据大小
_m = ati([0, 11, 100, 1E4]) 

# 这是一个图论题的数据生成器,该题目在洛谷的题号为P1339
for i in range(1, 4): # 即在[1, 4)范围内循环,也就是从1到3
    test_data = IO(file_prefix="heat", data_id=i) # 生成 heat[1|2|3].in/out 三组测试数据

    n = _n[i] # 点数
    m = _m[i] # 边数
    s = randint(1, n) # 源点,随机选取一个
    t = randint(1, n) # 汇点,随机选取一个
    test_data.input_writeln(n, m, s, t) # 写入到输入文件里,自动以空格分割并换行

    graph = Graph.graph(n, m, weight_limit=5) # 生成一个n点,m边的随机图,边权限制为5
    test_data.input_writeln(graph) # 自动写入到输入文件里,默认以一行一组u v w的形式输出

    test_data.output_gen("D:\\std_binary.exe") # 标程编译后的可执行文件,不需要freopen等,CYaRon自动给该程序输入并获得输出

输入输出 IO

IO库可以方便的帮您建立一组测试数据。构造函数的调用方法有以下几种:

IO("test1.in", "test1.out") # test1.in, test1.out
IO(file_prefix="test") # test.in, test.out
IO(file_prefix="test", data_id=3) # test3.in, test3.out
IO(file_prefix="test", data_id=6, input_suffix=".input", output_suffix=".answer") # test6.input, test6.answer
IO("test2.in") # test2.in, .out文件生成为临时文件
IO(file_prefix="test", data_id=5, disable_output=True) # test5.in, 不建立.out
IO() # .in, .out文件均生成为临时文件,一般配合对拍器使用

以上方法可以帮您创建一组测试数据的文件。其中使用file_prefixdata_id配合for循环可以较为方便地批量生成多个数据点。

IO库的方法主要有以下几种:

io = IO("test1.in", "test1.out") # 先新建一组数据
io.input_write(1, 2, 3) # 写入1 2 3到输入文件
io.input_writeln(4, 5, 6) # 写入4 5 6到输入文件并换行
io.output_write(1, 2, 3) # 写入1 2 3到输出文件
io.output_writeln(4, 5, 6) # 写入4 5 6到输出文件并换行
io.input_write([1, 2, 3]) # 写入1 2 3到输入文件
io.output_write(1, 2, [1, 2, 3], [4]) # 写入1 2 1 2 3 4到输出文件
io.input_write(1, 2, 3, separator=',') # 写入1,2,3,到输入文件,目前版本尾部会多一个逗号,之后可能修改行为
io.output_gen("~/Documents/std") # 执行shell命令或二进制文件,把输入文件的内容通过stdin送入,获得stdout的内容生成输出
io.output_gen("C:\\Users\\Aqours\\std.exe") # 当然Windows也可以

图 Graph

Graph库可以用来生成各种各样的树、图、链等结构。

这个库中本身已经配备了各种常用的图的模板,不过我们先来看看手动建立一个图的方法。

graph = Graph(10) # 建立一个10个节点的无向图
graph = Graph(10, directed=True) # 建立一个10个节点的有向图
# 这两个图的节点编号范围都为1到10

graph.add_edge(1, 5) # 建立一条从1到5,权值为1的边,若是无向图,还会建立从5到1的边
graph.add_edge(1, 6, weight=3) # 建立一条从1到6,权值为3的边,若是无向图,还会建立从6到1的边

graph.edges # 一个邻接表数组,每一维度i保存的是i点出发的所有边,以Edge对象存储
for edge in graph.iterate_edges(): # 遍历所有边,其中edge内保存的也是Edge对象
    edge.start # 获取这条边的起点
    edge.end # 获取这条边的终点
    edge.weight # 获取这条边的边权
    io.input_writeln(edge) # 输出这条边,以u v w的形式

io.input_writeln(graph) # 输出这个图,以每条边u v w一行的格式
io.input_writeln(graph.to_str(shuffle=True)) # 打乱边的顺序并输出这个图
io.input_writeln(graph.to_str(output=my_func)) # 使用my_func函数替代默认的输出函数,请查看源代码以理解使用方法
io.input_writeln(graph.to_str(output=Edge.unweighted_edge)) # 输出无权图,以每条边u v一行的格式

不过在大多数情况下您不需要手动建图,我们为您准备了大量模板,用法如下:

graph = Graph.graph(n, m) # 生成一个n点,m边的无向图,边权均为1
graph = Graph.graph(n, m, directed=True, weight_limit=(5, 300)) # 生成一个n点,m边的有向图,边权范围是5到300
graph = Graph.graph(n, m, weight_limit=20) # 生成一个n点,m边的无向图,边权范围是1到20
graph = Graph.graph(n, m, weight_gen=my_func) # 生成一个n点,m边的无向图,使用自定义随机函数my_func的返回值作为边权
graph = Graph.graph(n, m, self_loop=False, repeated_edges=False) # 生成一个n点,m边的无向图,禁止重边和自环
# 以上的directed, weight_limit, weight_gen参数,对如下的所有函数都有效。

chain = Graph.chain(n) # 生成一条n个节点的链,是Graph.tree(n, 1, 0)的别名
flower = Graph.flower(n) # 生成一朵n个节点的菊花图,是Graph.tree(n, 0, 1)的别名
tree = Graph.tree(n) # 生成一棵n个节点的随机树
tree = Graph.tree(n, 0.4, 0.35) # 生成一棵n个节点的树,其中40%的节点呈现链状,35%的节点呈现菊花图状,剩余25%的节点随机加入
binary_tree = Graph.binary_tree(n) # 生成一棵n个节点的随机二叉树
binary_tree = Graph.binary_tree(n, 0.4, 0.35) # 生成一棵n个节点的二叉树,其中节点有40%的概率是左儿子,35%的概率是右儿子,25%的概率被随机选择
graph = Graph.hack_spfa(n) # 生成一个n点,1.5*n(下取整)边的图,具有卡SPFA的特点
graph = Graph.hack_spfa(n, extra_edge=m) # 生成一个n点,1.5*n+m(下取整)边的图,具有卡SPFA的特点
# 下列方法生成的图保证连通
# 支持 self_loop, repeated_edges, weight_limit, weight_gen 参数,但不支持 directed,DAG 的 self_loop 默认为 False
graph = Graph.DAG(n, m) # 生成一个 n 点,m 边的有向无环图
graph = Graph.DAG(n, m, loop=True) # 生成一个 n 点,m 边的有向有环图
graph = Graph.UDAG(n, m) # 生成一个 n 点,m 边的无向联通图

多边形 Polygon

使用Polygon库您可以输入、生成多边形,并对其进行一些简单的操作。

p = Polygon([(0,0), (0,4), (4,4), (4,0)]) # 以这四个点生成四边形,注意点需要按照连线顺序
p.perimeter() # 周长
p.area() # 面积
io.input_writeln(p)

# 您也可以使用以下的模板生成随机的多边形
p = Polygon.convex_hull(n) # 生成一个N个点的凸包
p = Polygon.simple_polygon(n) # 生成一个N个点的简单多边型

有关于Polygon库的更多高级用法,请参见源代码。

向量 Vector

CYaRon的向量功能可以帮助您生成一个list,包括若干个向量。 在算法竞赛中,需要生成互不相同的坐标集,或者一组不重复的数列是很有用的。

用法:

list Vector.random(num=5, position_range=[10], mode=0)

一些范例:

output = Vector.random()
#默认值,随机生成5个[0,10]的不重复数字的数列。

output = Vector.random(10, [(10,50)])
#生成10个范围在[10,50]之间的不重复数字数列。

output = Vector.random(30, [(10,50), 20])
#生成30个第一维范围[10,50]之间、第二维范围在[0,20]之间的不重复的二维向量。

output = Vector.random(30, [(1,10), (1,10), (1,10)], 2)
#生成30个每一维范围[1,10]之间的三维实数向量。

output = Vector.random(30, [10], 1)
#生成30个[0,10]之间的随机数,当然肯定会有重复咯。

在不使用pypy的情况下,生成一组1e5个unique的二维向量,坐标值不超过1e9,大约需要10秒钟。生成向量的算法均摊复杂度大于O(num),小于O(num*log(num))

默认情况下,即使是一维数列,每一项数字也是一个列表。例如 [[7], [110], [230], [415]],如果需要展平成一个一维 list,可以使用 sum(output,[])

字符串 String

String库可以帮您生成各种随机字符串、单词、句子、段落等。

使用方法如下:

str = String.random(5) # 生成一个5个字母的单词,从小写字母中随机选择
str = String.random((10, 20), charset="abcd1234") # 生成一个10到20个字母之间的单词,从abcd1234共8个字符中随机选择
str = String.random(10, charset="#######...") # 生成一个10个字母的只有'#'和'.'组成的字符串,'#'的可能性是70%,'.'可能30%。
str = String.random(None, charset=["foo", "bar"]) # 从foo、bar两个单词中随机选择一个返回
# charset参数对于以下所有指令也有效。

str = String.random_sentence(5) # 生成一个5个单词的句子,以空格分割,第一个单词首字母自动大写,结尾有句号或感叹号,每个单词3到8个字母长
str = String.random_sentence((10, 20), word_separators=",;", sentence_terminators=None, first_letter_uppercase=False, word_length_range=(2, 10), charset="abcdefg") # 生成一个10到20个单词的句子,以逗号或分号随机分割,第一个单词首字母不大写,结尾没有任何符号,每个单词2到10字母长,从abcdefg共7个字符中随机选择
# 以上所有参数,对于以下所有指令也有效

str = String.random_paragraph((3, 10)) # 生成一个3到10个句子的段落,句子之间以句号或感叹号分割,小句之间以逗号或分号分割,句子和小句结束后均接有一个空格,句子开头首字母大写而小句开头首字母不大写。生成句子的可能性为30%而小句的可能性为70%。
str = String.random_paragraph(6, sentence_joiners="|", sentence_separators=",", sentence_terminators=".?", termination_percentage=0.1) # 生成一个6个句子的段落,句子之间以句号或问号号分割,小句之间以逗号分割,句子和小句结束后均接有一个"|"号,句子开头首字母大写而小句开头首字母不大写。生成句子的可能性为10%而小句的可能性为90%。

# 注意:如果您需要以两个空格分割单词,应该使用如下写法:
str = String.random_sentence(5, word_separators=["  "]) # 以两个空格分割单词
# 而不是:
str = String.random_sentence(5, word_separators="  ") # 这会导致从两个空格中随机选择一个,也就是只有一个空格

序列 Sequence

Sequence是一个可以用来通过一个函数或者一个表达式,制造各种序列的东西。

使用方法如下面代码所示:

Sequence(lambda i, f: 2*i+1) # f(i)=2*i+1
Sequence(lambda i, f: f(i-1) + 1, [0, 1]) # f(i)=f(i-1)+1, f(0)=0, f(1)=1
Sequence(lambda i, f: f(i-1) + 1, {100: 101, 102: 103}) # f(i)=f(i-1)+1, f(100)=101, f(102)=103

其第一个参数为一个lambda函数,该lambda函数的第一个参数i代表这是序列的第几项,而第二个参数f则是一个可以获取该数列任意一项的函数。

第二个参数则是一个数组或dict,默认为空,是该序列的初始值列表。当这个序列的表达式中需要使用到f(即,需要递归进去获取函数值)的时候,必须提供第二个参数,否则找不到初始值会陷入死循环。

我们可以对其做如下操作:

seq = Sequence(lambda i, f: f(i-1) + 2, [0, 2, 4])
seq.get(3) # 6
seq.get(4, 6) # [8, 10, 12]
io.input_write(seq.get(7, 10)) # 可以直接传递给IO库,写入14 16 18 20

对拍器 Compare

将对拍器与您的数据生成器结合使用,您可以方便地检测您的程序的正确性。

  1. 对拍输出文件
# 默认比较器为NOIP风格,忽略最后空行和行尾空格
Compare.output("1.out", "2.out", std="std.out") 
# 以std.out为标准,对比1.out和2.out的正确性

std_io = IO()
std_io.output_writeln(1, 2, 3) # 往std_io的output里写入一些东西
Compare.output("1.out", "2.out", std=std_io) 
# 以std_io这个IO对象中的output为标准,对拍1.out和2.out,
  1. 对拍程序
input_io = IO()
input_io.input_write("1111\n")

Compare.program("a.exe", input=input_io, std_program="std.exe") 
# 以input_io这个IO对象中的input为stdin输入。
# std.exe的输出为标准输出,以此为标准对拍a.exe的输出。

Compare.program("a.exe", "b.exe", input=input_io, std_program="std.exe") 
# 和上面的方法类似,但是你可以以std.exe为标准对拍多个程序输出。

Compare.program("a.exe", "b.exe", "c.exe", input="data.in", std="std.out") 
# 当然input也可以简单地是文件,并以std.out这个输出文件的内容来对a.exe, b.exe, c.exe对拍。
# 这里std也可以是IO对象。

while True:
    input_io = IO()
    input_io.input_writeln(randint(1,100))
    Compare.program("a.exe", "b.exe", input=input_io, std_program="std.exe") 
# 不断地生成测试数据(这里是1到100的随机数),然后放到a.exe,b.exe中,分别以std.exe为标准进行对拍比较
# CYaRon 现在使用多线程比较器,原 stop_on_incorrect 参数现已 deprecated 且无实际作用。
# 并在工作目录下输出a.exe.out, std.out, error_input.in三个文件方便您进一步调试。

对于对拍程序的附加提示:CYaRon将每一个参数视为shell命令执行,也就是说在Linux下有必要写成./a, ./b等。

  1. 使用其他比较器

CYaRon内置NOIPStyleFullText两种比较器,默认为NOIPStyle,更换比较器的方法如下:

Compare.program("a.exe", input=input_io, std_program="std.exe", grader="FullText")

您也可以自己撰写比较器,请参考如下的代码示例。

from cyaron import *
from cyaron.graders import CYaRonGraders

@CYaRonGraders.grader("MyGrader")
def my_grader(content, std):
    if is_correct: # 请自行改为判断是否正确的逻辑
        return True, None
    else:
        return False, "Answer incorrect!"

Compare.program("a.exe", input=input_io, std_program="std.exe", grader="MyGrader")

工具函数

CYaRon 提供了一些简单的工具函数。

  1. ati(array)

ati函数输入一个数组,将数组的每一个元素转换为整形数后返回。

因为Python的1E5此类表达式返回的是浮点值,因此使用这类表达式定义数据范围时,需要通过此函数处理数组。

_n = ati([0, 5, 100, 1E3, 1E5])
  1. randint(n, m)

为标准库random.randint的别名。randint接受两个参数,生成[n, m]范围内(既,包含nm)的随机整数。

randint(1, 5) # int in [1, 5]
  1. randrange([n,] m [,step])

为标准库random.randrange的别名。请查看Python文档获得更多信息。

  1. uniform(n, m)

为标准库random.uniform的别名。uniform接受两个参数,生成[n, m]范围内(既,包含nm)的随机浮点数。

uniform(1, 5) # float in [1, 5]
  1. choice(seq)

为标准库random.choice的别名。choice从给定的数组中随机选取一个值返回。

choice([1, 2, 3])
  1. random()

为标准库random.random的别名。random返回[0, 1)范围内(既,包含0但不包含1)的随机浮点数。

random() # float in [0, 1)

常用常数

CYaRon 提供了一些常用的常数。

  1. PI

即圆周率的值。3.1415926...

  1. E

即自然底数的值。2.7182818...

  1. ALPHABET_SMALL

一个字符串,包含所有的小写字母。"abcdefghijklmnopqrstuvwxyz"

  1. ALPHABET_CAPITAL

一个字符串,包含所有的大写字母。"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

5.ALPHABET

一个字符串,包含所有的字母。"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

6.NUMBERS

一个字符串,包含所有的数字。"0123456789"

数学函数

此处文档待补充,请先查看math.py中的注释。

提高运行效率

Python 是一门解释执行语言,因此用 Python 和 CYaRon 编写的数据生成器会比同样功能的 C++ 等语言编写的运行缓慢。

但是我们有一些简单的的方法能够最大限度地抹平这个差异,从而使您在能够快速编写生成器的同时也能使生成器的运行效率最高。

  1. 使用 PyPy

PyPy 是一个以 Python 编写的 Python 语言的 JIT 编译器。对于具有大量循环和重复操作的程序,使用 PyPy 可以让执行效率获得成倍的提升。

有关 PyPy 的安装和具体使用方法,请参见 https://pypy.org/。

  1. 在 Python2 中,使用xrange代替range

如果您使用 Python2 ,需要执行大循环时,可以使用xrange代替range函数,这可以避免一个很大的循环数组被生成。

数学相关

CYaRon 只提供了一个简单的数学库。如果您需要生成更多和数学相关的内容,您可以使用 NumPy 。

请访问 http://www.numpy.org 以了解更多信息。

稍后将在此搬运一个教程以及和 CYaRon 配合使用的方式。

如何做出贡献

感谢您对 CYaRon 的支持。如果您想要做出贡献,可以采用以下的方法之一。

  1. 提交 Issue

当您发现 CYaRon 中存在的 Bug 或者您认为可以加入的实用功能的时候,可以提出 Issue 。

在您提出 Issue 时,请遵守以下的原则:

  1. 提交 Pull Request

您也可以直接 Fork 代码后对不足之处进行改进。改进之后您可以提交 Pull Request ,请求将改进内容合并到主仓库。

在您提出 Pull Request 时,请遵守以下的原则:

光荣榜

Toto Lin edited this page on May 18, 2017 · 8 revisions