├── sort ├── __init__.py ├── bubble_sort │ ├── __init__.py │ ├── README.md │ └── bubble_sort.py ├── merge_sort │ ├── __init__.py │ ├── README.md │ └── merge_sort.py ├── common │ ├── __init__.py │ ├── README.md │ ├── get_input.py │ └── critical_op.py ├── counting_inversion │ ├── __init__.py │ ├── README.md │ └── counting_inversion.py ├── README.md ├── Makefile ├── generator.py └── check.py ├── prim_mst ├── __init__.py ├── README.md ├── Makefile ├── get_input.py ├── node.py ├── critical_op.py ├── prim_mst.py ├── check.py ├── generator.py └── min_heap.py ├── frequent_element ├── __init__.py ├── README.md ├── Makefile ├── get_input.py ├── check.py ├── critical_op.py ├── generator.py └── frequent_element.py ├── matrix_mult ├── __init__.py ├── README.md ├── Makefile ├── get_input.py ├── generator.py ├── check.py ├── critical_op.py └── matrix_mult.py ├── closest_pairs ├── metric.png ├── __init__.py ├── Makefile ├── get_input.py ├── point.py ├── README.md ├── check.py ├── metric.py ├── generator.py ├── critical_op.py └── closest_pairs.py ├── .github └── workflows │ └── pylint.yml ├── dijkstra ├── Makefile ├── get_input.py ├── node.py ├── critical_op.py ├── check.py ├── README.md ├── bellmanford.py ├── dijkstra.py ├── dijkstra_with_neg_edge.py ├── min_heap.py └── generator.py ├── README.md ├── .gitignore └── LICENSE /sort/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /prim_mst/__init__.py: -------------------------------------------------------------------------------- 1 | from .prim_mst import prim 2 | -------------------------------------------------------------------------------- /sort/bubble_sort/__init__.py: -------------------------------------------------------------------------------- 1 | from .bubble_sort import bubble_sort as sort 2 | -------------------------------------------------------------------------------- /sort/merge_sort/__init__.py: -------------------------------------------------------------------------------- 1 | from .merge_sort import merge_sort as sort 2 | -------------------------------------------------------------------------------- /frequent_element/__init__.py: -------------------------------------------------------------------------------- 1 | from .frequent_element import frequent_element 2 | -------------------------------------------------------------------------------- /sort/common/__init__.py: -------------------------------------------------------------------------------- 1 | from .critical_op import compare_op 2 | from .get_input import get_input 3 | -------------------------------------------------------------------------------- /sort/counting_inversion/__init__.py: -------------------------------------------------------------------------------- 1 | from .counting_inversion import counting_inversion as count_inv 2 | -------------------------------------------------------------------------------- /matrix_mult/__init__.py: -------------------------------------------------------------------------------- 1 | from .critical_op import mult_cost 2 | from .matrix_mult import matrix_mult_dp 3 | -------------------------------------------------------------------------------- /closest_pairs/metric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Disalg-ICS-NJU/algocentric/HEAD/closest_pairs/metric.png -------------------------------------------------------------------------------- /closest_pairs/__init__.py: -------------------------------------------------------------------------------- 1 | from .closest_pairs import closest_pairs, brute_force, divide_and_conquer 2 | from .point import Point 3 | from .critical_op import DistanceOP 4 | from .get_input import get_input 5 | -------------------------------------------------------------------------------- /sort/merge_sort/README.md: -------------------------------------------------------------------------------- 1 | # 归并排序 2 | 3 | ## 问题描述 4 | 5 | 给定一个未排序整数序列, 使用归并排序算法进行排序. 6 | 7 | ## 输入 8 | 9 | 仅一行内容, 由空格分隔的未排序整数序列, 例如: 10 | 11 | ```txt 12 | 2 4 6 1 3 5 13 | ``` 14 | 15 | 注意: 没有输入的整数个数提示. 16 | 17 | ## 输出 18 | 19 | 一行排序好由空格分隔的整数序列, 例如: 20 | 21 | ```txt 22 | 1 2 3 4 5 6 23 | ``` 24 | -------------------------------------------------------------------------------- /sort/counting_inversion/README.md: -------------------------------------------------------------------------------- 1 | # 逆序对计数 2 | 3 | ## 问题描述 4 | 5 | 在数组中的两个数字, 如果前面一个数字大于后面的数字, 则这两个数字组成一个逆序对. 6 | 给定一个整数序列, 计算出序列中逆序对的总数. 7 | 8 | ## 输入 9 | 10 | 仅一行内容, 由空格分隔的整数序列, 例如: 11 | 12 | ```txt 13 | 5 1 4 2 8 14 | ``` 15 | 16 | 注意: 没有输入的整数个数提示. 17 | 18 | ## 输出 19 | 20 | 一个自然数, 表示序列中逆序对的总数, 例如: 21 | 22 | ```txt 23 | 4 24 | ``` 25 | 26 | 解释: 逆序对为(5, 1), (5, 4), (5, 2), (4, 2), 总共4个. 27 | -------------------------------------------------------------------------------- /sort/bubble_sort/README.md: -------------------------------------------------------------------------------- 1 | # 冒泡排序 2 | 3 | ## 问题描述 4 | 5 | 给定一个未排序整数序列, 使用冒泡排序算法进行排序. 6 | 7 | ## 输入 8 | 9 | 仅一行内容,由空格分隔的未排序整数序列, 例如: 10 | 11 | ```txt 12 | 2 4 6 1 3 5 13 | ``` 14 | 15 | 注意: 没有输入的整数个数提示. 16 | 17 | ## 输出 18 | 19 | 一行排序好由空格分隔的整数序列, 例如: 20 | 21 | ```txt 22 | 1 2 3 4 5 6 23 | ``` 24 | 25 | ## 算法优化 26 | 27 | 可以增加一个flag记录这一趟是否交换元素, 如果没有交换则该list已排好序, 无需下一趟比较. 28 | [check.py](./check.py)脚本会同时展示优化后的算法比较操作的次数. 29 | -------------------------------------------------------------------------------- /sort/common/README.md: -------------------------------------------------------------------------------- 1 | # 常用的函数 2 | 3 | 此目录的python文件包含了一些可复用的函数: 4 | 5 | - get_input(): 从标准输入获取待排序的list 6 | - compare_op: 用于提供元素比较接口并维护比较操作的次数 7 | 8 | 可通过下面的语句将它们import: 9 | 10 | ```python 11 | import sys 12 | import os 13 | # 将sort目录放入python搜索路径中, 使得下面的 from common import ... 能成功执行. 14 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 15 | 16 | from common import get_input, compare_op # pylint: disable=wrong-import-position # noqa 17 | ``` 18 | -------------------------------------------------------------------------------- /matrix_mult/README.md: -------------------------------------------------------------------------------- 1 | # 矩阵连乘 2 | 3 | ## 问题描述 4 | 5 | 定n个矩阵:A[1],A[2],...,A[n],其中A[i]与A[i+1]是可乘的(i=1,2,...,n-1)。 6 | 7 | 确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少。 8 | 9 | 注意,根据矩阵乘法定义,两个矩阵相乘意味着后一矩阵的行数与前一矩阵的列数相同。 10 | 11 | ## 输入 12 | 13 | 两行内容, 第一行为矩阵个数n的值, 14 | 第二行为由空格分隔的整数序列, n+1个数依次是n个矩阵的行数以及最后一个矩阵的列数, 15 | 例如: 16 | 17 | ```txt 18 | 5 19 | 5 10 4 6 10 2 20 | ``` 21 | 22 | ## 输出 23 | 24 | 计算矩阵连乘积的计算次序和最少数乘次数, 例如: 25 | 26 | ```txt 27 | (A[0](A[1](A[2](A[3]A[4])))) 28 | 348 29 | ``` 30 | -------------------------------------------------------------------------------- /prim_mst/README.md: -------------------------------------------------------------------------------- 1 | # 最小生成树 Prim算法 2 | 3 | ## 问题描述 4 | 5 | 给定一个加权无向连通图, 使用Prim算法找出其任意一棵最小生成树. 6 | 7 | ## 输入 8 | 9 | 有若干行内容, 第一行为由空格隔开的两个整数, 分别表示图的点数 $n$ 和边数 $m$. 10 | 接下来有$m$行, 每一行为由空格隔开的三个自然数, 依次表示边的起点, 终点以及边的权重. 11 | 为了便于实现, 图中节点编号取值为$[0,n)$, 边的权重非负. 例如: 12 | 13 | ```txt 14 | 6 9 15 | 0 1 7 16 | 0 3 8 17 | 1 3 3 18 | 1 2 6 19 | 2 3 4 20 | 3 4 3 21 | 2 4 2 22 | 2 5 5 23 | 4 5 2 24 | ``` 25 | 26 | ## 输出 27 | 28 | 一个正整数, 表示Prim算法产生的最小生成树其边的权重和. 例如: 29 | 30 | ```txt 31 | 17 32 | ``` 33 | -------------------------------------------------------------------------------- /frequent_element/README.md: -------------------------------------------------------------------------------- 1 | # 常见元素寻找 2 | 3 | ## 问题描述 4 | 5 | 序列中的元素可能重复出现, 如果某一个元素的出现次数 $f\geq \lfloor\frac{n}{2}\rfloor+1$, 则该元素即为常见元素. 6 | 给定一个未排序的正整数序列, 找出所有常见元素. 7 | 8 | **问题升级**: 9 | 对于给定正整数常数 $k$, 某一个元素的出现次数 $f\geq \lfloor\frac{n}{k}\rfloor+1$, 则该元素即为常见元素. 10 | 给定一个未排序的正整数序列, 找出所有常见元素. 11 | 12 | ## 输入 13 | 14 | 两行内容, 第一行为由空格分隔的整数序列, 第二行为 $k$ 的值, 例如: 15 | 16 | ```txt 17 | 3 3 1 1 3 2 3 18 | 2 19 | ``` 20 | 21 | 注意: 没有输入的整数个数提示. 22 | 23 | ## 输出 24 | 25 | 一个有序序列, 表示序列中所有的常见元素, 例如: 26 | 27 | ```txt 28 | 3 29 | ``` 30 | -------------------------------------------------------------------------------- /matrix_mult/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | NUM ?= 20 3 | N ?= 13 4 | 5 | run: 6 | python3 ./matrix_mult.py 7 | 8 | test: 9 | python3 ./check.py 10 | 11 | test_no_input: 12 | @python3 ./generator.py $(NUM) -N $(N) | while read line1; do \ 13 | read line2; echo -e "$$line1\n$$line2" | python3 ./check.py; \ 14 | echo ----; \ 15 | done 16 | 17 | random_test: 18 | @time python3 ./generator.py $(NUM) -N $(N) | while read line1; do \ 19 | read line2; result=$$(echo -e "$$line1\n$$line2" | python3 ./check.py); \ 20 | if test "$$?" -ne 0; then \ 21 | echo "$$result"; \ 22 | exit 1; \ 23 | fi; \ 24 | echo -n .; \ 25 | done && \ 26 | echo "PASS" 27 | -------------------------------------------------------------------------------- /frequent_element/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | NUM ?= 20 3 | K ?= 13 4 | 5 | run: 6 | python3 ./frequent_element.py 7 | 8 | test: 9 | python3 ./check.py 10 | 11 | test_no_input: 12 | @python3 ./generator.py -l 1000 -L 2000 $(NUM) -K $(K) | while read line1; do \ 13 | read line2; echo -e "$$line1\n$$line2" | python3 ./check.py; \ 14 | echo ----; \ 15 | done 16 | 17 | random_test: 18 | @time python3 ./generator.py $(NUM) -K $(K) | while read line1; do \ 19 | read line2; result=$$(echo -e "$$line1\n$$line2" | python3 ./check.py); \ 20 | if test "$$?" -ne 0; then \ 21 | echo "$$result"; \ 22 | exit 1; \ 23 | fi; \ 24 | echo -n .; \ 25 | done && \ 26 | echo "PASS" 27 | -------------------------------------------------------------------------------- /sort/README.md: -------------------------------------------------------------------------------- 1 | # 排序算法 2 | 3 | ## 算法描述 4 | 5 | 本模块中实现了两类经典排序算法: 6 | 7 | 1. [冒泡排序](./bubble_sort/) 8 | 2. [归并排序](./merge_sort/) 9 | 10 | 以及基于排序算法实现特定需求: 11 | 12 | 1. [逆序对计数](./counting_inversion/): 基于归并排序. 13 | 14 | ## 测试 15 | 16 | [check.py](./check.py)脚本测试算法结果的正确性, 检查序列是否有序, 或检查逆序对计数是否正确. 17 | [generator.py](./generator.py)脚本产生多个随机输入序列. 18 | [Makefile](./Makefile)中定义了若干命令, 便于快速运行和测试算法. 19 | `make run`进行算法运行, 可以使用`TARGET`指定运行的算法, 例如`make run TARGET=merge_sort`. 20 | `make test`则对指定算法进行测试. 21 | `make test_no_input`和`make random_test`命令来测试随机输入是否能通过验证. 22 | 23 | ## 性能分析 24 | 25 | 排序算法中比较两元素大小是其关键操作, 通过统计比较操作次数可以对排序算法的性能有直观的了解. 26 | [check.py](./check.py)脚本在测试通过后会输出比较操作次数. 27 | -------------------------------------------------------------------------------- /sort/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | TARGET ?= bubble_sort 3 | NUM ?= 10 4 | 5 | run: 6 | python3 $(TARGET)/$(TARGET).py 7 | 8 | test: 9 | python3 ./check.py $(TARGET) 10 | 11 | test_no_input: 12 | @python3 ./generator.py -l 1000 -L 1000 $(NUM) | while read line; do \ 13 | echo "$$line" | python3 ./check.py $(TARGET); \ 14 | if test "$(TARGET)" = "bubble_sort"; then \ 15 | echo "After optimization:"; \ 16 | echo "$$line" | python3 ./check.py -o $(TARGET); \ 17 | fi; \ 18 | echo ----; \ 19 | done 20 | 21 | random_test: 22 | @time python3 ./generator.py $(NUM) | while read line; do \ 23 | result=$$(echo "$$line" | python3 ./check.py $(TARGET)); \ 24 | if test "$$?" -ne 0; then \ 25 | echo "$$result"; \ 26 | exit 1; \ 27 | fi; \ 28 | echo -n .; \ 29 | done && \ 30 | echo "PASS" 31 | -------------------------------------------------------------------------------- /prim_mst/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | NUM ?= 100 3 | NODE ?= 100 4 | EDGE ?= 5000 5 | 6 | run: 7 | python3 ./prim_mst.py 8 | 9 | test: 10 | python3 ./check.py 11 | 12 | 13 | test_no_input: 14 | @python3 ./generator.py -n $(NODE) -N $(NODE) -m $(EDGE) -M $(EDGE) $(NUM) \ 15 | | while read n m; do \ 16 | (echo "$$n $$m"; \ 17 | for ((i=1; i<=$$m; i++)); do \ 18 | read line1; \ 19 | echo "$$line1"; \ 20 | done) \ 21 | | python3 ./check.py ; \ 22 | echo ----; \ 23 | done 24 | 25 | random_test: 26 | @time python3 ./generator.py $(NUM) \ 27 | | while read n m; do \ 28 | result=$$((echo "$$n $$m"; \ 29 | for ((i=1; i<=$$m; i++)); do \ 30 | read line1; \ 31 | echo "$$line1"; \ 32 | done) | python3 ./check.py); \ 33 | if test "$$?" -ne 0; then \ 34 | echo "$$result"; \ 35 | exit 1; \ 36 | fi; \ 37 | echo -n .; \ 38 | done 39 | -------------------------------------------------------------------------------- /.github/workflows/pylint.yml: -------------------------------------------------------------------------------- 1 | name: Pylint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ["3.10"] 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v3 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | - name: Install dependencies 18 | run: | 19 | python -m pip install --upgrade pip 20 | pip install pylint matplotlib 21 | - name: Analysing the code with pylint 22 | run: | 23 | env PYTHONPATH=sort:frequent_element:prim_mst:matrix_mult:dijkstra:closest_pairs pylint --disable=missing-module-docstring --disable=missing-class-docstring --disable=missing-function-docstring --disable=duplicate-code $(git ls-files '*.py') 24 | -------------------------------------------------------------------------------- /dijkstra/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | NUM ?= 100 3 | NODE ?= 100 4 | EDGE ?= 500 5 | 6 | run: 7 | python3 ./dijkstra.py 8 | 9 | test: 10 | python3 ./check.py 11 | 12 | 13 | test_no_input: 14 | @python3 ./generator.py -n $(NODE) -N $(NODE) -m $(EDGE) -M $(EDGE) $(NUM) --has_negative_edges\ 15 | | while read n m s; do \ 16 | (echo "$$n $$m $$s "; \ 17 | for ((i=1; i<=$$m; i++)); do \ 18 | read line1; \ 19 | echo "$$line1"; \ 20 | done) \ 21 | | python3 ./check.py ; \ 22 | echo ----; \ 23 | done 24 | 25 | random_test: 26 | @time python3 ./generator.py $(NUM) \ 27 | | while read n m s; do \ 28 | result=$$((echo "$$n $$m $$s "; \ 29 | for ((i=1; i<=$$m; i++)); do \ 30 | read line1; \ 31 | echo "$$line1"; \ 32 | done) \ 33 | | python3 ./check.py); \ 34 | if test "$$?" -ne 0; then \ 35 | echo "$$result"; \ 36 | exit 1; \ 37 | fi; \ 38 | echo -n .; \ 39 | done 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 算法实验 2 | 3 | - 基本信息 4 | - 这个仓库主要包含授课中重要知识点、重点习题所对应的算法实现。 5 | - 持续建设中,仓库的基本内容、组织方式近期都会逐步调整(2023.3)。 6 | 7 | - 课程信息 8 | - 课程名称:算法设计与分析(22010030) 9 | - 授课对象:本科二年级下(南京大学,计算机系) 10 | - 授课教师:[黄宇](https://zhuanlan.zhihu.com/algocentric) 11 | 12 | ## 编程规范 13 | 14 | 推荐使用Python3语言完成实验, 编程规范请遵守[PEP 8](https://peps.python.org/pep-0008/). 15 | 常用的IDE(如[VS code](https://code.visualstudio.com/), [PyCharm](https://www.jetbrains.com/pycharm/)), 16 | 可以提供[linting](https://code.visualstudio.com/docs/python/linting)功能来帮助你识别和纠正语法和规范问题. 17 | 18 | ## 代码示例 19 | 20 | [冒泡排序](./sort/bubble_sort/) 21 | [归并排序](./sort/merge_sort/) 22 | [逆序对计数](./sort/counting_inversion/) 23 | [常见元素寻找](./frequent_element/) 24 | [最小生成树Prim算法](./prim_mst/) 25 | [矩阵相乘](./matrix_mult/) 26 | [有向图单源最短路](./dijkstra/) 27 | [寻找最近点对](./closest_pairs/) 28 | 29 | ## 授课资源 30 | 31 | [《算法设计与分析》课程材料](https://zhuanlan.zhihu.com/p/106049035) 32 | -------------------------------------------------------------------------------- /closest_pairs/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = /bin/bash 2 | NUM ?= 10 3 | 4 | run: 5 | python3 ./closest_pairs.py 6 | 7 | test: 8 | python3 ./check.py 9 | 10 | test_no_input: 11 | @python3 ./generator.py -n 100 $(NUM) -x -1000 -X 1000 -y -100 -Y 1000 | \ 12 | while read n; do \ 13 | points="$$n"; \ 14 | for i in $$(seq 1 $$n); do \ 15 | read line; \ 16 | points="$$points\n$$line"; \ 17 | done; \ 18 | echo -e "$$points" | python3 ./check.py; \ 19 | echo "----"; \ 20 | done 21 | 22 | random_test: 23 | @time python3 ./generator.py $(NUM) | \ 24 | while read n; do \ 25 | points="$$n"; \ 26 | for i in $$(seq 1 $$n); do \ 27 | read line; \ 28 | points="$$points\n$$line"; \ 29 | done; \ 30 | result=$$(echo -e "$$points" | python3 ./check.py); \ 31 | if test "$$?" -ne 0; then \ 32 | echo "$$result"; \ 33 | exit 1; \ 34 | fi; \ 35 | echo -n .; \ 36 | done && \ 37 | echo "PASS" 38 | 39 | 40 | -------------------------------------------------------------------------------- /sort/common/get_input.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | def get_input() -> list: 22 | '''从标准输入获取待排序的list, 输入为一行由空格隔开的待排序整数 23 | 24 | Returns: 25 | 待排序的list 26 | ''' 27 | 28 | return list(map(int, input().split())) 29 | -------------------------------------------------------------------------------- /frequent_element/get_input.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | def get_input() -> tuple[list, int]: 22 | '''从标准输入获取输入, 共两行内容, 第一行为由空格分隔的整数序列, 第二行为k值 23 | 24 | Returns: 25 | 返回整数序列和k值 26 | ''' 27 | array = list(map(int, input().split())) 28 | k = int(input()) 29 | return array, k 30 | -------------------------------------------------------------------------------- /matrix_mult/get_input.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # , Anpu Lu . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | def get_input() -> tuple[int, list]: 22 | '''从标准输入获取输入, 共两行内容, 第一行为k值, 第二行为由空格分隔的整数序列 23 | 24 | Returns: 25 | 返回n值和整数序列 26 | ''' 27 | k = int(input()) 28 | array = list(map(int, input().split())) 29 | return k, array 30 | -------------------------------------------------------------------------------- /prim_mst/get_input.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | def get_input() -> tuple[int, list[tuple]]: 23 | '''从标准输入获取加权无向连通图 24 | 25 | Returns: 26 | 图的节点数和所有边 27 | ''' 28 | node_num, edge_num = map(int, input().split()) 29 | edges = [] 30 | for _ in range(edge_num): 31 | edges.append(tuple(map(int, input().split()))) 32 | return node_num, edges 33 | -------------------------------------------------------------------------------- /dijkstra/get_input.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | def get_input() -> tuple[int, int, list[tuple]]: 23 | '''从标准输入获取加权有向图和源点 24 | 25 | Returns: 26 | 图的节点数,源节点和所有边 27 | ''' 28 | node_num, edge_num, start = map(int, input().split()) 29 | edges = [] 30 | for _ in range(edge_num): 31 | edges.append(tuple(map(int, input().split()))) 32 | 33 | return node_num, start, edges 34 | -------------------------------------------------------------------------------- /closest_pairs/get_input.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # , Peiyang He 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | from typing import List 22 | from point import Point 23 | 24 | 25 | def get_input() -> List[Point]: 26 | '''从标准输入获取输入, 第一行是点的数量n, 下面n行每行由一个空格分隔的两个整数组成, 代表点的横纵坐标 27 | 28 | Returns: 29 | 点结构体组成的列表 30 | ''' 31 | n = int(input().strip()) 32 | points = [] 33 | for _ in range(n): 34 | x, y = map(int, input().strip().split()) 35 | points.append(Point(x, y)) 36 | return points 37 | -------------------------------------------------------------------------------- /closest_pairs/point.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2025 Ruize Tang , Runze Wu 4 | # , Peiyang He 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | import math 22 | 23 | 24 | class Point: 25 | '''二维平面上的点类''' 26 | 27 | def __init__(self, x: int, y: int): 28 | '''生成一个点Point 29 | 30 | Args: 31 | x: 点的横坐标 32 | y: 点的纵坐标 33 | ''' 34 | self.x = x 35 | self.y = y 36 | 37 | def distance(self, other) -> float: 38 | """计算两个点的欧几里得距离""" 39 | return math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2) 40 | 41 | def __repr__(self): 42 | return f'Point({self.x}, {self.y})' 43 | -------------------------------------------------------------------------------- /prim_mst/node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | class Node: 23 | '''节点类 24 | 25 | 记录每个节点在Fringe中的权重 26 | ''' 27 | 28 | def __init__(self, node_id: int, point_to: int, weight: int): 29 | '''生成一个节点Node 30 | 31 | Args: 32 | id (int): 节点的id 33 | point_to(int): 指向的节点 34 | weight (int): 边的权重 35 | ''' 36 | self.node_id = node_id 37 | self.point_to = point_to 38 | self.weight = weight 39 | 40 | def __lt__(self, other) -> bool: 41 | '''实现__lt__自定义比较''' 42 | return self.weight < other.weight 43 | 44 | def __repr__(self) -> str: 45 | return f'{self.node_id}:{self.point_to} weight:{self.weight}' 46 | -------------------------------------------------------------------------------- /dijkstra/node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | class Node: 23 | '''节点类 24 | 25 | 记录每个节点在Fringe中的权重 26 | ''' 27 | 28 | def __init__(self, node_id: int, point_to: int, weight: int): 29 | '''生成一个节点Node 30 | 31 | Args: 32 | id (int): 节点的id 33 | point_to(int): 指向的节点 34 | weight (int): 边的权重 35 | ''' 36 | self.node_id = node_id 37 | self.point_to = point_to 38 | self.weight = weight 39 | self.dist= 0x7fffffff 40 | 41 | def __lt__(self, other) -> bool: 42 | '''实现__lt__自定义比较''' 43 | return self.dist < other.dist 44 | 45 | def __repr__(self) -> str: 46 | return f'{self.node_id}:{self.point_to} weight:{self.weight} dist:{self.dist}' 47 | -------------------------------------------------------------------------------- /closest_pairs/README.md: -------------------------------------------------------------------------------- 1 | # 寻找最近点对 2 | 3 | ## 问题描述 4 | 5 | 给定一组二维平面上的点, 找出其中距离最近的一对点, 输出这个最短距离. 6 | 7 | 其中「距离」定义为[欧几里得距离](https://en.wikipedia.org/wiki/Euclidean_distance). 8 | 9 | ## 输入 10 | 11 | 有若干行内容, 第一行为一个整数, 表示点的个数 $n$. 12 | 接下来有 $n$ 行, 其中每一行为由一个空格隔开的两个整数, 分别代表一个点的 $x$ 轴和 $y$ 轴坐标. 例如: 13 | 14 | ```text 15 | 5 16 | -34 -16 17 | 46 77 18 | 100 -37 19 | -99 -84 20 | -49 22 21 | ``` 22 | 23 | 注: 输入保证 $n \ge 2$ 成立. 24 | 25 | ## 输出 26 | 27 | 一个浮点数, 表示所有点对之间的最小距离. 例如: 28 | 29 | ```text 30 | 40.85339643163099 31 | ``` 32 | 33 | 注: 任何与正确答案误差小于等于 $10^{-9}$ 的输出都是可以接受的. 34 | 35 | ## 使用方法 36 | 37 | - 编写代码 38 | 39 | 你可以将你的实现写在 `closest_pairs.py` 的 `closest_pairs` 函数中, 但请保持函数签名不变. 40 | 该函数默认使用分治算法. 41 | 42 | 43 | - 获取输入 44 | 45 | 运行 `generator.py` 以获得随机输入. 首先执行如下命令查看帮助信息: 46 | 47 | ```sh 48 | python3 ./generator.py -h 49 | ``` 50 | 51 | 例如运行 52 | ```sh 53 | python3 ./generator.py -n 10 -x -1000 -X 1000 -y -1000 -Y 1000 2 54 | ``` 55 | 你将会得到 2 组随机测试用例, 每组测试用例由 10 个点组成, 其中横纵坐标的取值均在 $[-1000, 1000]$ 之间. 56 | 57 | 58 | - 测试脚本 59 | 60 | 在 Makefile 中定义了四个目标: 61 | 62 | - ``make run`` 63 | 64 | 输入一个测试用例, 将输出执行的结果. 65 | - ``make test`` 66 | 67 | 输入一个测试用例, 将使用对拍程序判断是否输出了正确的结果. 68 | - ``make test_no_input`` 69 | 70 | 使用对拍程序测试实现. 可修改 Makefile 中的 `NUM` 变量以指定测试用例的数量. 71 | - ``make random_test`` 72 | 73 | 使用对拍程序测试实现, 并输出相关的时间信息. 可修改 Makefile 中的 `NUM` 变量以指定测试用例的数量. 74 | 75 | - 关键操作次数对比 76 | 77 | 运行如下命令查看不同算法的关键操作次数的对比: 78 | ```sh 79 | python3 ./metric.py 80 | ``` 81 | 该程序将给出在不同规模的输入数据下, 暴力算法、分治算法和你的算法的关键次数的折线图. 82 | 例如下图所示, 对比了 $n$ 为 5, 10, 20, 50, 100, 150 时暴力算法和分治算法各自的关键操作次数: 83 | 84 | metric 85 | 86 | 可以发现, 对于输入规模为 $n$ 的暴力算法, 关键操作次数总为 $(n-1+1)*(n-1)/2$; 值得注意的是, 对于分治算法, 由于随机性的引入, 图中曲线难以复现. 87 | -------------------------------------------------------------------------------- /sort/bubble_sort/bubble_sort.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | import os 24 | # 将sort目录放入python搜索路径中, 使得下面的 from common import ... 能成功执行. 25 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 26 | 27 | from common import get_input, compare_op # pylint: disable=wrong-import-position # noqa 28 | 29 | 30 | def bubble_sort(array: list, optimize=False) -> list: 31 | '''冒泡排序算法. 32 | 33 | Args: 34 | array (list): 未排序的list 35 | optimize (bool, optional): 是否为优化冒泡排序算法. 默认为无优化版本 36 | 37 | Returns: 38 | 返回排序后的list 39 | ''' 40 | 41 | new_list = list(array) 42 | for i in range(len(new_list)-1): 43 | flag = False 44 | for j in range(0, len(new_list)-i-1): 45 | if compare_op(new_list[j], new_list[j+1]): 46 | flag = True 47 | new_list[j], new_list[j+1] = new_list[j+1], new_list[j] 48 | if optimize and flag is False: # 优化版本, 当一轮没有发生交换元素时, 算法即可终止. 49 | break 50 | return new_list 51 | 52 | 53 | if __name__ == '__main__': 54 | testcase = get_input() 55 | result = bubble_sort(testcase) 56 | print(' '.join(map(str, result))) 57 | -------------------------------------------------------------------------------- /sort/merge_sort/merge_sort.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | import os 24 | # 将sort目录放入python搜索路径中, 使得下面的 from common import ... 能成功执行. 25 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 26 | 27 | from common import get_input, compare_op # pylint: disable=wrong-import-position # noqa 28 | 29 | 30 | def merge_sort(array: list) -> list: 31 | '''归并排序算法. 32 | 33 | Args: 34 | array (list): 未排序的list 35 | 36 | Returns: 37 | 返回排序后的list 38 | ''' 39 | 40 | num = len(array) 41 | if num <= 1: 42 | return list(array) 43 | left = merge_sort(array[:num//2]) 44 | right = merge_sort(array[num//2:]) 45 | new_list = [] 46 | i, j = 0, 0 47 | while i < len(left) and j < len(right): 48 | if compare_op(left[i], right[j]): 49 | new_list.append(right[j]) 50 | j += 1 51 | else: 52 | new_list.append(left[i]) 53 | i += 1 54 | new_list += left[i:] + right[j:] 55 | return new_list 56 | 57 | 58 | if __name__ == '__main__': 59 | testcase = get_input() 60 | result = merge_sort(testcase) 61 | print(' '.join(map(str, result))) 62 | -------------------------------------------------------------------------------- /sort/common/critical_op.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | class CriticalOP(): 22 | '''算法关键操作类 23 | 24 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 25 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 26 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 27 | ''' 28 | 29 | def __init__(self): 30 | self.op_count = 0 31 | 32 | def get_op_count(self) -> int: 33 | '''获取关键操作的次数 34 | ''' 35 | return self.op_count 36 | 37 | def incr_op_count(self): 38 | '''增加关键操作的次数 39 | ''' 40 | self.op_count += 1 41 | 42 | def reset_op_count(self): 43 | '''重置关键操作的次数 44 | ''' 45 | self.op_count = 0 46 | 47 | 48 | class CompareOP(CriticalOP): 49 | '''比较操作类. 50 | 51 | 对于排序算法, 两元素的大小比较是其关键操作. 52 | 该类用于提供元素比较接口并维护比较操作的次数. 53 | ''' 54 | 55 | def __call__(self, elem_a: int, elem_b: int) -> bool: 56 | '''排序算法中的比较操作. 57 | 58 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 59 | 60 | Args: 61 | elem_a (int): 待比较元素elem_a 62 | elem_b (int): 待比较元素elem_b 63 | 64 | Returns: 65 | bool: 若a大于b则返回True, 否则为False 66 | ''' 67 | self.incr_op_count() 68 | return elem_a > elem_b 69 | 70 | 71 | compare_op = CompareOP() 72 | -------------------------------------------------------------------------------- /dijkstra/critical_op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | class CriticalOP(): 23 | '''算法关键操作类 24 | 25 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 26 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 27 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 28 | ''' 29 | 30 | def __init__(self): 31 | self.op_count = 0 32 | 33 | def get_op_count(self) -> int: 34 | '''获取关键操作的次数 35 | ''' 36 | return self.op_count 37 | 38 | def incr_op_count(self): 39 | '''增加关键操作的次数 40 | ''' 41 | self.op_count += 1 42 | 43 | def reset_op_count(self): 44 | '''重置关键操作的次数 45 | ''' 46 | self.op_count = 0 47 | 48 | 49 | class CompareOP(CriticalOP): 50 | '''比较操作类. 51 | 52 | 对于排序算法, 两元素的大小比较是其关键操作. 53 | 该类用于提供元素比较接口并维护比较操作的次数. 54 | ''' 55 | 56 | def __call__(self, elem_a: int, elem_b: int) -> bool: 57 | '''排序算法中的比较操作. 58 | 59 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 60 | 61 | Args: 62 | elem_a (int): 待比较元素elem_a 63 | elem_b (int): 待比较元素elem_b 64 | 65 | Returns: 66 | bool: 若elem_a大于elem_b则返回True, 否则为False 67 | ''' 68 | self.incr_op_count() 69 | return elem_a > elem_b 70 | 71 | 72 | compare_op = CompareOP() 73 | -------------------------------------------------------------------------------- /prim_mst/critical_op.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | class CriticalOP(): 23 | '''算法关键操作类 24 | 25 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 26 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 27 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 28 | ''' 29 | 30 | def __init__(self): 31 | self.op_count = 0 32 | 33 | def get_op_count(self) -> int: 34 | '''获取关键操作的次数 35 | ''' 36 | return self.op_count 37 | 38 | def incr_op_count(self): 39 | '''增加关键操作的次数 40 | ''' 41 | self.op_count += 1 42 | 43 | def reset_op_count(self): 44 | '''重置关键操作的次数 45 | ''' 46 | self.op_count = 0 47 | 48 | 49 | class CompareOP(CriticalOP): 50 | '''比较操作类. 51 | 52 | 对于排序算法, 两元素的大小比较是其关键操作. 53 | 该类用于提供元素比较接口并维护比较操作的次数. 54 | ''' 55 | 56 | def __call__(self, elem_a: int, elem_b: int) -> bool: 57 | '''排序算法中的比较操作. 58 | 59 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 60 | 61 | Args: 62 | elem_a (int): 待比较元素elem_a 63 | elem_b (int): 待比较元素elem_b 64 | 65 | Returns: 66 | bool: 若elem_a大于elem_b则返回True, 否则为False 67 | ''' 68 | self.incr_op_count() 69 | return elem_a > elem_b 70 | 71 | 72 | compare_op = CompareOP() 73 | -------------------------------------------------------------------------------- /sort/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from random import randint 23 | import argparse 24 | 25 | 26 | def get_random_input(length: int, random_range: tuple) -> list: 27 | '''获取随机数list 28 | 29 | Args: 30 | length (int): 随机数list的长度 31 | random_range (tuple): 随机数的大小范围 32 | 33 | Returns: 34 | 随机数list 35 | ''' 36 | 37 | random_list = [] 38 | for _ in range(length): 39 | random_list.append(randint(*random_range)) 40 | return random_list 41 | 42 | 43 | if __name__ == '__main__': 44 | parser = argparse.ArgumentParser(description="Genrate random test inputs") 45 | parser.add_argument('-l', dest='min_len', type=int, default=0, 46 | help="min list length") 47 | parser.add_argument('-L', dest='max_len', type=int, default=1000, 48 | help="max list length") 49 | parser.add_argument('-m', dest='min', type=int, default=-5000, 50 | help="min random number") 51 | parser.add_argument('-M', dest='max', type=int, default=5000, 52 | help="max random number") 53 | parser.add_argument(dest='num', type=int, default=10, nargs='?', 54 | help="number of test input") 55 | args = parser.parse_args() 56 | random_length = randint(args.min_len, args.max_len) 57 | for _ in range(args.num): 58 | new_list = get_random_input(random_length, (args.min, args.max)) 59 | print(' '.join(map(str, new_list))) 60 | -------------------------------------------------------------------------------- /matrix_mult/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # , Anpu Lu . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from random import randint 23 | import argparse 24 | 25 | 26 | def get_random_input(k: int, random_range: tuple) -> list: 27 | '''获取随机数list 28 | 29 | Args: 30 | k (int): 矩阵个数 31 | random_range (tuple): 随机数的大小范围 32 | 33 | Returns: 34 | 随机数n和随机数list 35 | ''' 36 | random_list = [] 37 | for _ in range(k + 1): 38 | random_list.append(randint(*random_range)) 39 | return random_list 40 | 41 | 42 | if __name__ == '__main__': 43 | parser = argparse.ArgumentParser(description="Genrate random test inputs") 44 | parser.add_argument('-m', dest='min', type=int, default=1, 45 | help="min random number") 46 | parser.add_argument('-M', dest='max', type=int, default=5000, 47 | help="max random number") 48 | parser.add_argument('-n', dest='min_n', type=int, default=2, 49 | help="min random n") 50 | parser.add_argument('-N', dest='max_n', type=int, default=20, 51 | help="max random n") 52 | parser.add_argument(dest='num', type=int, default=10, nargs='?', 53 | help="number of test input") 54 | args = parser.parse_args() 55 | for _ in range(args.num): 56 | n = randint(args.min_n, args.max_n) 57 | new_list = get_random_input(n, (args.min, args.max)) 58 | print(n) 59 | print(' '.join(map(str, new_list))) 60 | -------------------------------------------------------------------------------- /sort/counting_inversion/counting_inversion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | import os 24 | # 将sort目录放入python搜索路径中, 使得下面的 from common import ... 能成功执行. 25 | sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 26 | 27 | from common import get_input, compare_op # pylint: disable=wrong-import-position # noqa 28 | 29 | 30 | def counting_inversion(array: list) -> int: 31 | '''归并排序统计逆序对算法. 32 | 33 | Args: 34 | array (list): 未排序的list 35 | 36 | Returns: 37 | 返回list中的逆序对个数 38 | ''' 39 | def merge_sort(array: list) -> tuple[list, int]: 40 | num = len(array) 41 | if num <= 1: 42 | return list(array), 0 43 | left, left_cnt = merge_sort(array[:num//2]) 44 | right, right_cnt = merge_sort(array[num//2:]) 45 | inv_count = left_cnt+right_cnt 46 | new_list = [] 47 | i, j = 0, 0 48 | while i < len(left) and j < len(right): 49 | if compare_op(left[i], right[j]): 50 | new_list.append(right[j]) 51 | j += 1 52 | inv_count += len(left)-i 53 | else: 54 | new_list.append(left[i]) 55 | i += 1 56 | new_list += left[i:] + right[j:] 57 | return new_list, inv_count 58 | _, inv_count = merge_sort(array) 59 | return inv_count 60 | 61 | 62 | if __name__ == '__main__': 63 | testcase = get_input() 64 | result = counting_inversion(testcase) 65 | print(result) 66 | -------------------------------------------------------------------------------- /dijkstra/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 24 | from critical_op import compare_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 25 | from bellmanford import bellman_ford 26 | 27 | USE_NEGATIVE_EDGES = True 28 | 29 | if USE_NEGATIVE_EDGES: 30 | from dijkstra_with_neg_edge import dijkstra 31 | else: 32 | from dijkstra import dijkstra 33 | 34 | 35 | def check(oracle, to_check) -> bool: 36 | '''检查oracle和to_check是否完全一致. 37 | 38 | Args: 39 | oracle: 给定的正确结果 40 | to_check: 待验证的输入 41 | 42 | Returns: 43 | oracle是否与to_check完全一致的检查结果 44 | ''' 45 | if oracle is None: 46 | return False 47 | if len(oracle)!=len(to_check): 48 | return False 49 | if oracle!=to_check: 50 | return False 51 | return True 52 | 53 | 54 | if __name__ == '__main__': 55 | testcase = get_input() 56 | result = dijkstra(testcase[0], testcase[1], testcase[2]) 57 | ref = bellman_ford(testcase[0],testcase[1],testcase[2]) 58 | 59 | if check(ref, result): 60 | print('PASS') 61 | print('Critical op counts in Dijkstra:', compare_op.get_op_count()) 62 | else: 63 | print('FAIL') 64 | print('Input :', testcase) 65 | if ref is None: 66 | print("检测到负权环") 67 | sys.exit(1) 68 | for i, j in zip(ref, result): 69 | print(i, j) 70 | sys.exit(1) 71 | -------------------------------------------------------------------------------- /frequent_element/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | from collections import Counter 24 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 25 | from critical_op import equal_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 26 | from frequent_element import frequent_element 27 | 28 | 29 | def check(oracle, to_check) -> bool: 30 | '''检查oracle和to_check是否完全一致. 31 | 32 | Args: 33 | oracle: 给定的正确结果 34 | to_check: 待验证的输入 35 | 36 | Returns: 37 | oracle是否与to_check完全一致的检查结果 38 | ''' 39 | return oracle == to_check 40 | 41 | 42 | def frequent_element_check(array: list, k: int, to_check: list) -> bool: 43 | '''检查to_check是否为正确的常见元素 44 | 45 | Args: 46 | array (list): 给定的输入list 47 | k (int): 给定的正整数常数k 48 | to_check (list): 待检查的常见元素序列 49 | 50 | Returns: 51 | to_check是否为正确的常见元素 52 | ''' 53 | counter = Counter(array) 54 | oracle = [elem for elem, freq in counter.items() if freq >= 55 | len(array)//k+1] 56 | return check(sorted(oracle), to_check) 57 | 58 | 59 | if __name__ == '__main__': 60 | testcase = get_input() 61 | result = frequent_element(testcase[0], testcase[1]) 62 | if frequent_element_check(testcase[0], testcase[1], result): 63 | print('PASS') 64 | print('Critical op counts:', equal_op.get_op_count()) 65 | else: 66 | print('FAIL') 67 | print('Input :', testcase) 68 | print('Result:', result) 69 | sys.exit(1) 70 | -------------------------------------------------------------------------------- /dijkstra/README.md: -------------------------------------------------------------------------------- 1 | # 单源最短路 Dijkstra 算法 2 | 3 | ## 问题描述 4 | 5 | 给定一个加权有向图和源节点 $s$, 使用 Dijkstra 算法找出 $s$ 到所有节点的最短路. 6 | 7 | ## 输入 8 | 9 | 有若干行内容, 第一行为由空格隔开的三个整数, 分别表示图的点数 $n$ 、边数 $m$ 和源节点 $s$. 10 | 接下来有 $m$ 行, 每一行为由空格隔开的三个自然数, 依次表示边的起点, 终点以及边的权重. 11 | 为了便于实现, 图中节点编号取值为 $[0,n)$ , 边的权重非负. 例如: 12 | 13 | ```txt 14 | 4 6 0 15 | 0 1 2 16 | 1 2 2 17 | 1 3 1 18 | 0 2 5 19 | 2 3 3 20 | 0 3 4 21 | ``` 22 | 23 | ## 输出 24 | 25 | 一行 $n$ 个数,第 $i$ 个数表示 $s$ 到 $i$ 的最短路的距离. 若点 $i$ 不可达,输出 2147483647 (即 0x7fffffff) 26 | 27 | ```txt 28 | 0 2 4 3 29 | ``` 30 | 31 | ## 对于示例代码的解释 32 | 33 | ### 辅助方法 34 | 35 | `generator.py` 用于随机生成输入数据,其参数及解释如下: 36 | ``` 37 | usage: generator.py [-h] [-n MIN_NODE] [-N MAX_NODE] [-m MIN] [-M MAX] [--has_negative_edges] [--has_negative_cycle] [num] 38 | 39 | Genrate random test inputs 40 | 41 | positional arguments: 42 | num number of test input 43 | 44 | options: 45 | -h, --help show this help message and exit 46 | -n MIN_NODE min node number 47 | -N MAX_NODE max node number 48 | -m MIN min edge number 49 | -M MAX max edge number 50 | --has_negative_edges Whether the graph can have negative edges 51 | --has_negative_cycle Whether the graph can have negative cycles 52 | ``` 53 | 54 | 55 | `get_input.py` 用于从 `stdin` 读取输入并格式化为 `dijkstra` 和 `bellman-ford` 接受的形式。 56 | 57 | `node.py` 、`min_heap.py` 和 `critical_op.py` 提供了在堆操作时对关键操作(比较操作)的计数。 58 | 59 | `bellman-ford.py` 作为检查代码正确性的参考。 60 | 61 | 62 | ### 核心算法 63 | 64 | `dijkstra.py` 是不含负权边的带权有向图单源最短路算法的实现。 65 | 66 | `dijkstra_with_neg_edge.py` 是对原算法的修改,可以正确处理存在负权边(但不含负权环)的情况。 67 | 68 | ### 测试工具 69 | 70 | `check.py` 用于检查 `dijkstra` 的实现。使用的正确参考是 `bellman-ford` 算法。对于错误的结果,输出第一行是输入数据,之后 `n` 行每行两个数,第 `i` 行的两个数分别是 `dijkstra` 和 `bellman-ford` 算法得出的,从源节点到节点 `i` 的最短路径长度。如需使用 `dijkstra_with_neg_edge.py`,将第 27 行修改为: 71 | ``` 72 | USE_NEGATIVE_EDGES = True 73 | ``` 74 | 75 | `Makefile` 提供了一键回归测试的快捷指令。可以使用 76 | ``` 77 | make run 78 | ``` 79 | 运行 `dijkstra.py`,这个选项需要手动输入符合格式的数据。 80 | 81 | 使用 82 | ``` 83 | make test 84 | ``` 85 | 运行 `check.py`,调用你的实现和参考实现比对,同样需要手动输入符合格式的数据。 86 | 87 | 使用 88 | ``` 89 | make test_no_input 90 | ``` 91 | 自动生成数据并进行测试。在 `Makefile` 第 14 行中,你可以自行修改为 `generator.py` 输入的参数来获得不同的随机生成的数据。比如可以删去 `--has_negative_edges` 来生成不含负权边的有向图。各个参数的定义见 [`generator.py --help`](#辅助方法) 92 | 93 | 使用 94 | ``` 95 | make random_test [NUM=100] 96 | ``` 97 | 来进行随机的若干组测试,可以通过 `NUM=100` 显式地指定随机测试的规模。 -------------------------------------------------------------------------------- /closest_pairs/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2025 Ruize Tang , Runze Wu 5 | # , Peiyang He 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import math 23 | import sys 24 | from typing import List 25 | from critical_op import DistanceOP # pylint: disable=wrong-import-position, no-name-in-module # noqa 26 | from get_input import get_input # pylint: disable=wrong-import-position, no-name-in-module # noqa 27 | from point import Point # pylint: disable=wrong-import-position, no-name-in-module # noqa 28 | from closest_pairs import closest_pairs, brute_force # pylint: disable=wrong-import-position, no-name-in-module # noqa 29 | 30 | 31 | def check(oracle, to_check) -> bool: 32 | '''检查oracle和to_check是否完全一致. 33 | 34 | Args: 35 | oracle: 给定的正确结果 36 | to_check: 待验证的输入 37 | 38 | Returns: 39 | oracle是否与to_check完全一致的检查结果 40 | ''' 41 | return oracle == to_check 42 | 43 | 44 | def closest_pairs_check(array: List[Point], to_check: float) -> bool: 45 | '''使用O(n^2)的暴力算法检查to_check是否是点对之间的最小距离 46 | 47 | Args: 48 | array (list): 给定的输入list 49 | to_check (float): 待检查的点对之间的最小距离 50 | 51 | Returns: 52 | to_check是否为正确的点对之间的最小距离 53 | ''' 54 | return math.isclose(brute_force(array, DistanceOP())[0], to_check, rel_tol=1e-9) 55 | 56 | 57 | if __name__ == '__main__': 58 | testcase = get_input() 59 | (result, cnt) = closest_pairs(testcase) 60 | if closest_pairs_check(testcase, result): 61 | print('PASS') 62 | print('Critical op counts:', cnt) 63 | else: 64 | print('FAIL') 65 | print('Input :', testcase) 66 | print('Result:', result) 67 | sys.exit(1) 68 | -------------------------------------------------------------------------------- /frequent_element/critical_op.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | class CriticalOP(): 22 | '''算法关键操作类 23 | 24 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 25 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 26 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 27 | ''' 28 | 29 | def __init__(self): 30 | self.op_count = 0 31 | 32 | def get_op_count(self) -> int: 33 | '''获取关键操作的次数 34 | ''' 35 | return self.op_count 36 | 37 | def incr_op_count(self): 38 | '''增加关键操作的次数 39 | ''' 40 | self.op_count += 1 41 | 42 | def reset_op_count(self): 43 | '''重置关键操作的次数 44 | ''' 45 | self.op_count = 0 46 | 47 | 48 | class CompareOP(CriticalOP): 49 | '''比较操作类. 50 | 51 | 对于排序算法, 两元素的大小比较是其关键操作. 52 | 该类用于提供元素比较接口并维护比较操作的次数. 53 | ''' 54 | 55 | def __call__(self, elem_a: int, elem_b: int) -> bool: 56 | '''排序算法中的比较操作. 57 | 58 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 59 | 60 | Args: 61 | elem_a (int): 待比较元素elem_a 62 | elem_b (int): 待比较元素elem_b 63 | 64 | Returns: 65 | bool: 若elem_a大于elem_b则返回True, 否则为False 66 | ''' 67 | self.incr_op_count() 68 | return elem_a > elem_b 69 | 70 | 71 | class EqualOP(CriticalOP): 72 | '''相等操作类 73 | 74 | 对于寻找常见元素算法, 判断两元素是否相等是其关键操作. 75 | 该类用于提供判断元素相等接口并维护比较操作的次数. 76 | ''' 77 | 78 | def __call__(self, elem_a: int, elem_b: int) -> bool: 79 | '''判断元素相等操作. 80 | 81 | Args: 82 | elem_a (int): 待判断元素elem_a 83 | elem_b (int): 待判断元素elem_b 84 | 85 | Returns: 86 | bool: 若elem_a等于elem_b则返回True, 否则为False 87 | ''' 88 | self.incr_op_count() 89 | return elem_a == elem_b 90 | 91 | 92 | equal_op = EqualOP() 93 | -------------------------------------------------------------------------------- /dijkstra/bellmanford.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Jingbo Zhai <306330361@qq.com> 5 | # 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import os 23 | import sys 24 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 25 | 26 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 27 | 28 | # 单源最短路的正确性判定,使用 Bellman-Ford 算法 29 | class Graph: 30 | def __init__(self, vertices): 31 | self.vertices = vertices 32 | self.graph = [] 33 | 34 | def add_edge(self, vertice, point_to, weight): 35 | self.graph.append([vertice, point_to, weight]) 36 | #self.graph.append([v, u, w]) # 对于含负权的无向图,不能使用bellman-ford 37 | 38 | def bellman_ford(self, src): 39 | dist = [0x7fffffff] * self.vertices 40 | dist[src] = 0 41 | 42 | # 松弛操作,重复执行 V-1 次 43 | for _ in range(self.vertices - 1): 44 | for vertice, point_to, weight in self.graph: 45 | if dist[vertice] != 0x7fffffff and dist[vertice] + weight < dist[point_to]: 46 | dist[point_to] = dist[vertice] + weight 47 | 48 | # 检查负权环 49 | for vertice, point_to, weight in self.graph: 50 | if dist[vertice] != 0x7fffffff and dist[vertice] + weight < dist[point_to]: 51 | return None 52 | # 如果不存在负权环,则打印最短路径 53 | return dist 54 | 55 | def bellman_ford(node_num: int,src: int,edges: list[tuple]): 56 | graph = Graph(node_num) 57 | 58 | # 添加边 59 | for vertice, point_to, weight in edges: 60 | graph.add_edge(vertice, point_to, weight) 61 | 62 | # 运行贝尔曼-福特算法 63 | return graph.bellman_ford(src) 64 | 65 | if __name__ == '__main__': 66 | testcase = get_input() 67 | result,opcnt = bellman_ford(testcase[0], testcase[1], testcase[2]) 68 | if result is None: 69 | print("检测到负权环") 70 | else: 71 | print("源点到所有点的最短路径距离:", result) 72 | -------------------------------------------------------------------------------- /matrix_mult/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # , Anpu Lu . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 24 | from critical_op import mult_cost # pylint: disable=wrong-import-position, no-name-in-module # noqa 25 | from matrix_mult import matrix_mult_dp 26 | 27 | 28 | def check(oracle, to_check) -> bool: 29 | '''检查oracle和to_check是否完全一致. 30 | 31 | Args: 32 | oracle: 给定的正确结果 33 | to_check: 待验证的输入 34 | 35 | Returns: 36 | oracle是否与to_check完全一致的检查结果 37 | ''' 38 | return oracle == to_check 39 | 40 | 41 | def matrix_mult_check(dime_list: list, to_check: int) -> bool: 42 | '''检查to_check是否为正确的最小开销 43 | 44 | Args: 45 | dime_list (list): 给定的输入list 46 | to_check (list): 待检查的最小开销 47 | 48 | Returns: 49 | to_check是否为正确的最小开销 50 | ''' 51 | return check(matrix_mult_bf(dime_list), to_check) 52 | 53 | 54 | def matrix_mult_bf(dime_list: list) -> int: 55 | k = len(dime_list) - 1 56 | if k <= 1: 57 | return 0 58 | cost = float('inf') 59 | for i in range(1, k): 60 | cost1 = dime_list[i-1] * dime_list[i] * dime_list[i+1] 61 | new_list = dime_list.copy() 62 | new_list.pop(i) 63 | cost2 = matrix_mult_bf(new_list) 64 | cost = min(cost, cost1 + cost2) 65 | return cost 66 | 67 | 68 | if __name__ == '__main__': 69 | testcase = get_input() 70 | result = matrix_mult_dp(testcase[0], testcase[1]) 71 | if matrix_mult_check(testcase[1], result[1]): 72 | print('PASS') 73 | print('Critical op counts:', mult_cost.get_op_count()) 74 | else: 75 | print('FAIL') 76 | print('Input :', testcase) 77 | print('Result:', result) 78 | sys.exit(1) 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # VS code 132 | .vscode/ 133 | 134 | # MacOS 135 | .DS_Store 136 | 137 | #JetBrains IDEs 138 | .idea/ 139 | -------------------------------------------------------------------------------- /closest_pairs/metric.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2025 Ruize Tang , Runze Wu 5 | # , Peiyang He 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import os 23 | import subprocess 24 | import sys 25 | from typing import List 26 | from matplotlib import pyplot as plt 27 | from get_input import get_input # pylint: disable=wrong-import-position, no-name-in-module # noqa 28 | from closest_pairs import Point, brute_force, divide_and_conquer, closest_pairs, \ 29 | DistanceOP # pylint: disable=wrong-import-position, no-name-in-module # noqa 30 | 31 | 32 | def generate_and_get_input(num: int) -> List[Point]: 33 | tmp_file = "tmp" 34 | # 重定向标准输出到临时文件 35 | with open(tmp_file, "w", encoding="utf-8") as f: 36 | subprocess.run(["python3", "generator.py", "-n", str(num)], stdout=f, text=True, check=True) 37 | with open(tmp_file, "r", encoding="utf-8") as f: 38 | sys.stdin = f 39 | res = get_input() 40 | os.remove(tmp_file) 41 | return res 42 | 43 | 44 | if __name__ == '__main__': 45 | n_to_test = [10, 20, 50, 100, 150] 46 | results = {"brute_force": [], "yours": [], "divide_and_conquer": []} 47 | for n in n_to_test: 48 | x = generate_and_get_input(n) 49 | results["brute_force"].append(brute_force(x, DistanceOP())[1]) 50 | results["yours"].append(closest_pairs(x)[1]) 51 | results["divide_and_conquer"].append(divide_and_conquer(x)[1]) 52 | 53 | # 折线图 54 | plt.figure(figsize=(10, 9)) 55 | for algo, color in zip(results.keys(), ['b', 'r', 'g']): 56 | plt.plot(n_to_test, results[algo], marker='o', label=algo, color=color) 57 | for i, txt in enumerate(results[algo]): 58 | plt.text(n_to_test[i], results[algo][i], str(txt), ha='right', va='bottom') 59 | 60 | plt.xlabel("Number of points") 61 | plt.ylabel("Critical operation count") 62 | plt.xticks(n_to_test) # 只显示 n_to_test 作为横坐标刻度 63 | plt.yscale('log') # 使用对数尺度, 避免左下方点重合 64 | plt.legend() 65 | plt.grid(True) 66 | plt.show() 67 | -------------------------------------------------------------------------------- /frequent_element/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from random import randint 23 | import argparse 24 | 25 | 26 | def get_random_input(length: int, random_range: tuple, given_k: int) -> list: 27 | '''获取随机数list 28 | 29 | Args: 30 | length (int): 随机数list的长度 31 | random_range (tuple): 随机数的大小范围 32 | given_k(tuple): 给定的常数k 33 | 34 | Returns: 35 | 随机数list 36 | ''' 37 | candidate_list = [] 38 | for _ in range(given_k): 39 | candidate_list.append(randint(*random_range)) 40 | random_list = [] 41 | for _ in range(length): 42 | random_list.append(candidate_list[randint(0, given_k-1)]) 43 | return random_list 44 | 45 | 46 | if __name__ == '__main__': 47 | parser = argparse.ArgumentParser(description="Genrate random test inputs") 48 | parser.add_argument('-l', dest='min_len', type=int, default=0, 49 | help="min list length") 50 | parser.add_argument('-L', dest='max_len', type=int, default=1000, 51 | help="max list length") 52 | parser.add_argument('-m', dest='min', type=int, default=-5000, 53 | help="min random number") 54 | parser.add_argument('-M', dest='max', type=int, default=5000, 55 | help="max random number") 56 | parser.add_argument('-k', dest='min_k', type=int, default=2, 57 | help="min random k") 58 | parser.add_argument('-K', dest='max_k', type=int, default=20, 59 | help="max random k") 60 | parser.add_argument(dest='num', type=int, default=10, nargs='?', 61 | help="number of test input") 62 | args = parser.parse_args() 63 | for _ in range(args.num): 64 | random_length = randint(args.min_len, args.max_len) 65 | k = randint(args.min_k, args.max_k) 66 | new_list = get_random_input(random_length, (args.min, args.max), k) 67 | print(' '.join(map(str, new_list))) 68 | print(k) 69 | -------------------------------------------------------------------------------- /prim_mst/prim_mst.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from collections import defaultdict 23 | import os 24 | import sys 25 | # 将prim目录放入python搜索路径中, 使得下面的 from ... import ... 能成功执行. 26 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 27 | 28 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 29 | from min_heap import MinHeap # pylint: disable=wrong-import-position # noqa 30 | from node import Node # pylint: disable=wrong-import-position # noqa 31 | 32 | 33 | def prim(node_num: int, edges: list[tuple]) -> int: 34 | '''采用最小堆实现的prim算法 35 | 36 | Args: 37 | node_num (int): 图的节点数 38 | edges (list[tuple]): 图所有的边 39 | 40 | Returns: 41 | 最小生成树对应的权重 42 | ''' 43 | adjacent_dict = defaultdict(list[Node]) # 注意:defaultdict(list)必须以list做为变量 44 | for node_1, node_2, weight in edges: 45 | adjacent_dict[node_1].append(Node(node_2, node_1, weight)) 46 | adjacent_dict[node_2].append(Node(node_1, node_2, weight)) 47 | 48 | start = 0 # 以编号为0的节点作为Prim算法起始点 49 | visited = [False for _ in range(node_num)] # 是否访问过该节点 50 | visited[0] = True 51 | total_weight = 0 # mst的权重 52 | mst_node = {0} # 当前mst中具有的节点 53 | 54 | heap = MinHeap(node_num) 55 | for neighbor in adjacent_dict[start]: 56 | heap.insert(neighbor) 57 | visited[neighbor.node_id] = True 58 | 59 | while len(heap) > 0 and len(mst_node) < node_num: 60 | node = heap.extract_min() 61 | mst_node.add(node.node_id) # 该节点加入mst 62 | total_weight += node.weight # 加入该节点的权重 63 | for neighbor in adjacent_dict[node.node_id]: 64 | if visited[neighbor.node_id] is False: # 尚未访问过该节点 65 | heap.insert(neighbor) 66 | visited[neighbor.node_id] = True 67 | elif neighbor.node_id not in mst_node: # 节点在Fringe中, 考虑更新其权重 68 | heap.decrease_key(neighbor) 69 | 70 | assert len(mst_node) == node_num # 连通图总能找到生成树 71 | return total_weight 72 | 73 | 74 | if __name__ == '__main__': 75 | testcase = get_input() 76 | result = prim(testcase[0], testcase[1]) 77 | print(result) 78 | -------------------------------------------------------------------------------- /closest_pairs/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2025 Ruize Tang , Runze Wu 5 | # , Peiyang He 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from random import randint 23 | import argparse 24 | 25 | 26 | def get_random_input(num_points: int, x_range: tuple, y_range: tuple) -> list: 27 | '''获取随机的点集, 保证结果长度大于1且其中没有相同的点 28 | 29 | Args: 30 | num_points (int): 生成的点的数量 31 | x_range (tuple): 横坐标的取值范围 32 | y_range (tuple): 纵坐标的取值范围 33 | 34 | Returns: 35 | 点集 36 | ''' 37 | random_list = set() 38 | while len(random_list) < num_points: 39 | new_point = (randint(*x_range), randint(*y_range)) 40 | random_list.add(new_point) # 使用 set 自动去重 41 | return list(random_list) 42 | 43 | 44 | if __name__ == '__main__': 45 | parser = argparse.ArgumentParser(description="Generate random test inputs") 46 | parser.add_argument('-n', dest='num_points', type=int, default=100, 47 | help="number of points") 48 | parser.add_argument('-x', dest='min_x', type=int, default=-10000, 49 | help="min x value") 50 | parser.add_argument('-X', dest='max_x', type=int, default=10000, 51 | help="max x value") 52 | parser.add_argument('-y', dest='min_y', type=int, default=-10000, 53 | help="min y value") 54 | parser.add_argument('-Y', dest='max_y', type=int, default=10000, 55 | help="max y value") 56 | parser.add_argument(dest='num', type=int, default=10, nargs='?', 57 | help="number of test input") 58 | args = parser.parse_args() 59 | if args.num_points < 2: 60 | parser.error("number of points must be greater than 2") 61 | if args.min_x > args.max_x: 62 | parser.error("min_x must be smaller than or equal to max_x") 63 | if args.min_y > args.max_y: 64 | parser.error("min_y must be smaller than or equal to max_y") 65 | if (args.max_x - args.min_x) * (args.max_y - args.min_y) < args.num_points: 66 | parser.error("cannot generate distinct points of the required number") 67 | for _ in range(args.num): 68 | new_list = ( 69 | get_random_input(args.num_points, (args.min_x, args.max_x), (args.min_y, args.max_y))) 70 | print(args.num_points) 71 | for point in new_list: 72 | print(str(point[0]) + " " + str(point[1])) 73 | -------------------------------------------------------------------------------- /dijkstra/dijkstra.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from collections import defaultdict 23 | import os 24 | import sys 25 | # 将prim目录放入python搜索路径中, 使得下面的 from ... import ... 能成功执行. 26 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 27 | 28 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 29 | from min_heap import MinHeap # pylint: disable=wrong-import-position # noqa 30 | from node import Node # pylint: disable=wrong-import-position # noqa 31 | 32 | 33 | def dijkstra(node_num: int,start: int, edges: list[tuple]) -> int: 34 | '''采用最小堆实现的dijkstra算法 35 | 36 | Args: 37 | node_num (int): 图的节点数 38 | start (int): 源节点 39 | edges (list[tuple]): 图所有的边 40 | 41 | Returns: 42 | 从源节点到所有点的最短路径长度 43 | ''' 44 | adjacent_dict = defaultdict(list[Node]) # 注意:defaultdict(list)必须以list做为变量 45 | for node_1, node_2, weight in edges: 46 | adjacent_dict[node_1].append(Node(node_2, node_1, weight)) 47 | #adjacent_dict[node_2].append(Node(node_1, node_2, weight)) #无向图则取消该注释 48 | 49 | visited = [False for _ in range(node_num)] # 是否访问过该节点 50 | visited[start] = True 51 | dij_node = {start} # 当前确定的最短路径目标节点 52 | results=[0x7fffffff for _ in range(node_num)] 53 | results[start]=0 54 | heap = MinHeap(node_num) 55 | for neighbor in adjacent_dict[start]: 56 | neighbor.dist=results[start]+neighbor.weight 57 | heap.insert(neighbor) 58 | visited[neighbor.node_id] = True 59 | 60 | while len(heap) > 0 and len(dij_node) < node_num: 61 | node = heap.extract_min() 62 | dij_node.add(node.node_id) # 该节点加入dij_node 63 | results[node.node_id]=node.dist 64 | for neighbor in adjacent_dict[node.node_id]: 65 | if visited[neighbor.node_id] is False: # 尚未访问过该节点 66 | neighbor.dist=results[neighbor.point_to]+neighbor.weight 67 | heap.insert(neighbor) 68 | visited[neighbor.node_id] = True 69 | elif neighbor.node_id not in dij_node: # 节点在Fringe中, 考虑更新其权重 70 | neighbor.dist=min(neighbor.dist,neighbor.weight+results[neighbor.point_to]) 71 | heap.decrease_key(neighbor) 72 | 73 | return results 74 | 75 | 76 | if __name__ == '__main__': 77 | testcase = get_input() 78 | result = dijkstra(testcase[0], testcase[1], testcase[2]) 79 | print(*result) 80 | -------------------------------------------------------------------------------- /frequent_element/frequent_element.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import os 23 | import sys 24 | # 将sort目录放入python搜索路径中, 使得下面的 from ... import ... 能成功执行. 25 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 26 | 27 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 28 | from critical_op import equal_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 29 | 30 | 31 | def count_frequent(array: list, target: int) -> int: 32 | '''计算特定元素在序列中的频率. 33 | 34 | Args: 35 | array (list): 给定序列list 36 | target (int): 给定序列中特定元素 37 | 38 | Returns: 39 | 返回出现频率 40 | ''' 41 | freq = 0 42 | for elem in array: 43 | if equal_op(elem, target): 44 | freq += 1 45 | return freq 46 | 47 | 48 | def frequent_element(array: list, k: int) -> list: 49 | '''常见元素查找算法. 50 | 51 | Args: 52 | array (list): 待查找list 53 | k (int): 正整数常数k 54 | 55 | Returns: 56 | list: 所有常见元素的有序序列 57 | ''' 58 | if k <= 0: 59 | assert False, "k should be positive number!" 60 | if len(array) <= k: 61 | return sorted(set(array)) 62 | if k == 2: # 对于k=2的情况, 我们实现了摩尔投票算法来单独处理 63 | return find_majority(array) 64 | 65 | # 对于k>2的一般情况, 采取分治思想 66 | # 局部常见元素可能为全局常见元素 67 | # 而局部不常见则必不是全局常见 68 | ans = set() 69 | mid = len(array)//2+1 70 | 71 | left = frequent_element(array[:mid], k) 72 | right = frequent_element(array[mid:], k) 73 | 74 | for elem in left+right: 75 | if count_frequent(array, elem) >= len(array)//k+1: 76 | ans.add(elem) 77 | 78 | return sorted(ans) 79 | 80 | 81 | def find_majority(array: list) -> list: 82 | '''摩尔投票寻找大多数算法 83 | 84 | Args: 85 | array (list): 待查找list 86 | 87 | Returns: 88 | 返回包含大多数的列表, 不存在返回空 89 | ''' 90 | major, freq = 0, 0 91 | for elem in array: 92 | if freq == 0: 93 | major = elem 94 | if equal_op(elem, major): 95 | freq += 1 96 | else: 97 | freq -= 1 98 | if count_frequent(array, major) >= len(array)//2+1: 99 | return [major] 100 | return [] 101 | 102 | 103 | if __name__ == '__main__': 104 | testcase = get_input() 105 | result = frequent_element(testcase[0], testcase[1]) 106 | print(' '.join(map(str, result))) 107 | -------------------------------------------------------------------------------- /matrix_mult/critical_op.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # , Anpu Lu . 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | class CriticalOP(): 22 | '''算法关键操作类 23 | 24 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 25 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 26 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 27 | ''' 28 | 29 | def __init__(self): 30 | self.op_count = 0 31 | 32 | def get_op_count(self) -> int: 33 | '''获取关键操作的次数 34 | ''' 35 | return self.op_count 36 | 37 | def incr_op_count(self): 38 | '''增加关键操作的次数 39 | ''' 40 | self.op_count += 1 41 | 42 | def reset_op_count(self): 43 | '''重置关键操作的次数 44 | ''' 45 | self.op_count = 0 46 | 47 | 48 | class CompareOP(CriticalOP): 49 | '''比较操作类. 50 | 51 | 对于排序算法, 两元素的大小比较是其关键操作. 52 | 该类用于提供元素比较接口并维护比较操作的次数. 53 | ''' 54 | 55 | def __call__(self, elem_a: int, elem_b: int) -> bool: 56 | '''排序算法中的比较操作. 57 | 58 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 59 | 60 | Args: 61 | elem_a (int): 待比较元素elem_a 62 | elem_b (int): 待比较元素elem_b 63 | 64 | Returns: 65 | bool: 若elem_a大于elem_b则返回True, 否则为False 66 | ''' 67 | self.incr_op_count() 68 | return elem_a > elem_b 69 | 70 | 71 | class EqualOP(CriticalOP): 72 | '''相等操作类 73 | 74 | 对于寻找常见元素算法, 判断两元素是否相等是其关键操作. 75 | 该类用于提供判断元素相等接口并维护比较操作的次数. 76 | ''' 77 | 78 | def __call__(self, elem_a: int, elem_b: int) -> bool: 79 | '''判断元素相等操作. 80 | 81 | Args: 82 | elem_a (int): 待判断元素elem_a 83 | elem_b (int): 待判断元素elem_b 84 | 85 | Returns: 86 | bool: 若elem_a等于elem_b则返回True, 否则为False 87 | ''' 88 | self.incr_op_count() 89 | return elem_a == elem_b 90 | 91 | 92 | class MultCost(CriticalOP): 93 | '''相乘操作类 94 | 95 | 对于矩阵连乘算法, 计算两个矩阵相乘是其关键操作. 96 | 该类用于提供判断元素相等接口并维护比较操作的次数. 97 | ''' 98 | 99 | def __call__(self, elem_a: int, elem_b: int, elem_c: int) -> int: 100 | '''判断元素相等操作. 101 | 102 | Args: 103 | elem_a (int): 待乘元素elem_a 104 | elem_b (int): 待乘元素elem_b 105 | elem_c (int): 待乘元素elem_c 106 | 107 | Returns: 108 | int: 元素相乘结果 109 | ''' 110 | self.incr_op_count() 111 | return elem_a * elem_b * elem_c 112 | 113 | 114 | mult_cost = MultCost() 115 | -------------------------------------------------------------------------------- /closest_pairs/critical_op.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (c) 2023 Ruize Tang , Runze Wu 4 | # , Peiyang He 5 | # 6 | # This file is a part of Disalg-ICS-NJU/algocentric. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, version 3. 11 | # 12 | # This program is distributed in the hope that it will be useful, but 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | # 20 | 21 | from point import Point 22 | 23 | 24 | class CriticalOP(): 25 | '''算法关键操作类 26 | 27 | 我们实现的算法中, 一般都要进行某类操作来保证算法运行. 28 | 例如排序算法中, 我们要进行两个元素的比较; 之后学到的图算法中, 往往涉及对节点或边的染色等等 29 | 这类操作通常决定了算法的时间复杂度, 我们通过该类来维护算法关键操作的次数. 30 | ''' 31 | 32 | def __init__(self): 33 | self.op_count = 0 34 | 35 | def get_op_count(self) -> int: 36 | '''获取关键操作的次数 37 | ''' 38 | return self.op_count 39 | 40 | def incr_op_count(self): 41 | '''增加关键操作的次数 42 | ''' 43 | self.op_count += 1 44 | 45 | def reset_op_count(self): 46 | '''重置关键操作的次数 47 | ''' 48 | self.op_count = 0 49 | 50 | 51 | class CompareOP(CriticalOP): 52 | '''比较操作类. 53 | 54 | 对于排序算法, 两元素的大小比较是其关键操作. 55 | 该类用于提供元素比较接口并维护比较操作的次数. 56 | ''' 57 | 58 | def __call__(self, elem_a: int, elem_b: int) -> bool: 59 | '''排序算法中的比较操作. 60 | 61 | 比较两元素大小, 同时记录排序算法中元素比较这一关键操作的次数, 便于性能分析. 62 | 63 | Args: 64 | elem_a (int): 待比较元素elem_a 65 | elem_b (int): 待比较元素elem_b 66 | 67 | Returns: 68 | bool: 若elem_a大于elem_b则返回True, 否则为False 69 | ''' 70 | self.incr_op_count() 71 | return elem_a > elem_b 72 | 73 | 74 | class EqualOP(CriticalOP): 75 | '''相等操作类 76 | 77 | 对于寻找常见元素算法, 判断两元素是否相等是其关键操作. 78 | 该类用于提供判断元素相等接口并维护比较操作的次数. 79 | ''' 80 | 81 | def __call__(self, elem_a: int, elem_b: int) -> bool: 82 | '''判断元素相等操作. 83 | 84 | Args: 85 | elem_a (int): 待判断元素elem_a 86 | elem_b (int): 待判断元素elem_b 87 | 88 | Returns: 89 | bool: 若elem_a等于elem_b则返回True, 否则为False 90 | ''' 91 | self.incr_op_count() 92 | return elem_a == elem_b 93 | 94 | 95 | class DistanceOP(CriticalOP): 96 | '''计算两点距离类 97 | 98 | 对于最近点对问题, 求取两点之间的距离是其关键操作. 99 | 该类用于提供计算两个点之间的欧几里得距离的接口并维护操作的次数. 100 | ''' 101 | 102 | def __call__(self, elem_a: Point, elem_b: Point) -> float: 103 | '''计算两点之间的欧几里得距离 104 | 105 | Args: 106 | elem_a (Point): 点a 107 | elem_b (Point): 点b 108 | 109 | Returns: 110 | float: 点a与点b之间的欧几里得距离 111 | ''' 112 | self.incr_op_count() 113 | return elem_a.distance(elem_b) 114 | 115 | # 以往的代码是在本文件中创建一个全局的 *OP 实例, 但由于今后需要对比不同实现的关键操作次数, 所以创建 *OP 实例的职责交给不同的实现 116 | -------------------------------------------------------------------------------- /closest_pairs/closest_pairs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2025 Ruize Tang , Runze Wu 5 | # , Peiyang He 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import os 23 | import sys 24 | from typing import List 25 | 26 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 27 | from point import Point # pylint: disable=wrong-import-position, no-name-in-module # noqa 28 | from get_input import get_input # pylint: disable=wrong-import-position, no-name-in-module # noqa 29 | from critical_op import DistanceOP # pylint: disable=wrong-import-position, no-name-in-module # noqa 30 | 31 | 32 | def brute_force(array: List[Point], distance_op: DistanceOP) -> (float, int): 33 | # O(n^2)的暴力算法, 枚举每一个可能的点对距离 34 | n = len(array) 35 | min_distance = float('inf') 36 | for i in range(n): 37 | for j in range(i + 1, n): 38 | dist = distance_op(array[i], array[j]) 39 | min_distance = min(min_distance, dist) 40 | return min_distance, distance_op.get_op_count() 41 | 42 | 43 | def closest_pairs_recursion(array: List[Point], distance_op: DistanceOP) -> float: 44 | n = len(array) 45 | if n <= 3: 46 | # 对于n<=3的情况, 使用暴力算法直接求解 47 | return brute_force(array, distance_op)[0] 48 | mid = n // 2 49 | mid_point = array[mid] 50 | l = closest_pairs_recursion(array[:mid], distance_op) 51 | r = closest_pairs_recursion(array[mid:], distance_op) 52 | delta = min(l, r) 53 | ans = delta 54 | # 处理跨越中间点的情形 55 | slab = [] 56 | for _, point in enumerate(array): 57 | if abs(point.x - mid_point.x) <= delta: 58 | slab.append(point) 59 | slab = sorted(slab, key=lambda p: p.y) 60 | for (i, point) in enumerate(slab): 61 | for j in range(i + 1, min(len(slab), i + 8)): 62 | ans = min(ans, distance_op(point, slab[j])) 63 | return ans 64 | 65 | 66 | def divide_and_conquer(array: List[Point]) -> (float, int): 67 | array = sorted(array, key=lambda p: p.x) 68 | distance_op = DistanceOP() 69 | return closest_pairs_recursion(array, distance_op), distance_op.get_op_count() 70 | 71 | 72 | def closest_pairs(array: List[Point]) -> (float, int): 73 | # 你可以修改 closet_pairs 为你的实现, 但请保持函数签名不变 74 | # 第一个返回值返回点对之间的最小距离, 第二个返回值返回关键操作(求距离)的次数 75 | # 求取两个 Point 之间的距离时, 请调用 distance_op 方法, 例如: 76 | # distance_op = DistanceOP() 77 | # dist = distance_op(p, q) 78 | # 由于 DistanceOP 实例内部维护了计数值, 因此请确保在你的实现中只创建了一个 DistanceOP 实例 79 | return divide_and_conquer(array) 80 | 81 | 82 | if __name__ == '__main__': 83 | testcase = get_input() 84 | result = closest_pairs(testcase) 85 | print(result[0]) 86 | -------------------------------------------------------------------------------- /sort/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | import argparse 24 | from common import get_input, compare_op 25 | 26 | 27 | def check(oracle, to_check) -> bool: 28 | '''检查oracle和to_check是否完全一致. 29 | 30 | Args: 31 | oracle: 给定的正确结果 32 | to_check: 待验证的输入 33 | 34 | Returns: 35 | oracle是否与to_check完全一致的检查结果 36 | ''' 37 | return oracle == to_check 38 | 39 | 40 | def sort_check(array: list, to_check: list) -> bool: 41 | '''检查给定to_check是否为array排序后的结果. 42 | 43 | Args: 44 | array (list): 未排序的原始输入list 45 | to_check (list): 待检查的list 46 | 47 | Returns: 48 | to_check是否为array排序后的结果 49 | ''' 50 | oracle = sorted(list(array)) 51 | return check(oracle, to_check) 52 | 53 | 54 | def count_inv_check(array: list, to_check: int) -> bool: 55 | '''检查to_check是否为array中的逆序对个数 56 | 57 | Args: 58 | array (list): 未排序的原始输入list 59 | to_check (int): 待比对的逆序对个数 60 | 61 | Returns: 62 | to_check是否为array中的逆序对个数 63 | ''' 64 | oracle = 0 65 | for i in range(len(array)-1): 66 | for j in range(i+1, len(array)): 67 | if array[i] > array[j]: 68 | oracle += 1 69 | return check(oracle, to_check) 70 | 71 | 72 | if __name__ == '__main__': 73 | parser = argparse.ArgumentParser(description="Test sort algorithm") 74 | parser.add_argument('-o', dest='optimize', action='store_true', 75 | help="enable optimization for bubble_sort") 76 | parser.add_argument(dest='target', default='bubble_sort', nargs='?', 77 | help="target dir") 78 | args = parser.parse_args() 79 | if args.target == 'counting_inversion': 80 | target_alg = __import__(args.target).count_inv 81 | target_check = count_inv_check 82 | else: 83 | if args.target == 'bubble_sort': 84 | import bubble_sort 85 | from functools import partial 86 | target_alg = partial(bubble_sort.sort, optimize=args.optimize) 87 | else: 88 | target_alg = __import__(args.target).sort 89 | target_check = sort_check 90 | 91 | testcase = get_input() 92 | result = target_alg(testcase) 93 | if target_check(testcase, result): 94 | print('PASS') 95 | print('Critical op counts:', compare_op.get_op_count()) 96 | else: 97 | print('FAIL') 98 | print('Input :', testcase) 99 | print('Result:', result) 100 | sys.exit(1) 101 | -------------------------------------------------------------------------------- /matrix_mult/matrix_mult.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # , Anpu Lu . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import os 23 | import sys 24 | 25 | # 将matrix目录放入python搜索路径中, 使得下面的 from ... import ... 能成功执行. 26 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 27 | 28 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 29 | from critical_op import mult_cost # pylint: disable=wrong-import-position, no-name-in-module # noqa 30 | 31 | 32 | def matrix_mult_dp(k: int, dime_list: list) -> tuple[str, int]: 33 | '''计算矩阵连乘结果. 34 | 35 | Args: 36 | k (int): 矩阵个数 37 | dime_list (list): 给定矩阵信息list 38 | 39 | Returns: 40 | 返回最优顺序连乘和最小结果 41 | ''' 42 | cost = [[0 for _ in range(k + 1)] for _ in range(k + 1)] 43 | last = [[0 for _ in range(k + 1)] for _ in range(k + 1)] 44 | for low in range(k - 1, -1, -1): 45 | for high in range(low + 1, k + 1): 46 | if high - low == 1: 47 | best_cost = 0 48 | best_last = -1 49 | else: 50 | best_cost = float('inf') 51 | best_last = low 52 | for mid in range(low + 1, high): 53 | cost0 = cost[low][mid] 54 | cost1 = cost[mid][high] 55 | cost2 = mult_cost( 56 | dime_list[low], dime_list[mid], dime_list[high]) 57 | if cost0 + cost1 + cost2 < best_cost: 58 | best_cost = cost0 + cost1 + cost2 59 | best_last = mid 60 | cost[low][high] = best_cost 61 | last[low][high] = best_last 62 | return extract_order(last, k), cost[0][k] 63 | 64 | 65 | def extract_order(last: list, k: int) -> str: 66 | '''求连乘顺序. 67 | 68 | Args: 69 | last (list): DP计算结果 70 | k (int): 矩阵个数n 71 | 72 | Returns: 73 | list: 最终连乘顺序 74 | ''' 75 | que_mult_order = "" 76 | que_mult_order = extract(que_mult_order, last, 0, k) 77 | return que_mult_order 78 | 79 | 80 | def extract(que_mult_order: str, last: list, low: int, high: int) -> str: 81 | '''求连乘顺序的具体操作. 82 | 83 | Args: 84 | que_mult_order (str): 待生成结果 85 | last (list): DP计算结果 86 | low (int): 子序列头下标 87 | high (int): 子序列尾下标 88 | 89 | Returns: 90 | str: 连乘顺序 91 | ''' 92 | if high - low > 1: 93 | que_mult_order += '(' 94 | k = last[low][high] 95 | que_mult_order = extract(que_mult_order, last, low, k) 96 | que_mult_order = extract(que_mult_order, last, k, high) 97 | que_mult_order += ')' 98 | else: 99 | que_mult_order += 'A[' + str(low) + ']' 100 | return que_mult_order 101 | 102 | 103 | if __name__ == '__main__': 104 | testcase = get_input() 105 | result = matrix_mult_dp(testcase[0], testcase[1]) 106 | print(result[0]) 107 | print(result[1]) 108 | -------------------------------------------------------------------------------- /dijkstra/dijkstra_with_neg_edge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from collections import defaultdict 23 | import os 24 | import sys 25 | from critical_op import compare_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 26 | # 将prim目录放入python搜索路径中, 使得下面的 from ... import ... 能成功执行. 27 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 28 | 29 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 30 | from min_heap import MinHeap # pylint: disable=wrong-import-position # noqa 31 | from node import Node # pylint: disable=wrong-import-position # noqa 32 | 33 | 34 | def dijkstra(node_num: int,start: int, edges: list[tuple]) -> int: 35 | '''采用最小堆实现的dijkstra算法 36 | 37 | Args: 38 | node_num (int): 图的节点数 39 | start (int): 源节点 40 | edges (list[tuple]): 图所有的边 41 | 42 | Returns: 43 | 从源节点到所有点的最短路径长度 44 | ''' 45 | adjacent_dict = defaultdict(list[Node]) # 注意:defaultdict(list)必须以list做为变量 46 | for node_1, node_2, weight in edges: 47 | adjacent_dict[node_1].append(Node(node_2, node_1, weight)) 48 | #adjacent_dict[node_2].append(Node(node_1, node_2, weight)) #无向图则取消该注释 49 | 50 | visited = [False for _ in range(node_num)] # 是否访问过该节点 51 | visited[start] = True 52 | dij_node = {start} # 当前确定的最短路径目标节点 53 | results=[0x7fffffff for _ in range(node_num)] 54 | results[start]=0 55 | heap = MinHeap(node_num) 56 | for neighbor in adjacent_dict[start]: 57 | neighbor.dist=results[start]+neighbor.weight 58 | heap.insert(neighbor) 59 | visited[neighbor.node_id] = True 60 | 61 | while compare_op(len(heap) , 0): 62 | node = heap.extract_min() 63 | dij_node.add(node.node_id) # 该节点加入dij_node 64 | results[node.node_id]=node.dist 65 | for neighbor in adjacent_dict[node.node_id]: 66 | if visited[neighbor.node_id] is False: # 尚未访问过该节点 67 | neighbor.dist=results[neighbor.point_to]+neighbor.weight 68 | heap.insert(neighbor) 69 | visited[neighbor.node_id] = True 70 | elif neighbor.node_id not in dij_node: # 节点在Fringe中, 考虑更新其权重 71 | if compare_op(neighbor.dist,neighbor.weight+results[neighbor.point_to]): 72 | neighbor.dist=neighbor.weight+results[neighbor.point_to] 73 | heap.decrease_key(neighbor) 74 | elif compare_op(results[neighbor.node_id],neighbor.weight+results[neighbor.point_to]): 75 | neighbor.dist=neighbor.weight+results[neighbor.point_to] 76 | if heap.node_pos[neighbor.node_id]==-1: 77 | heap.insert(neighbor) 78 | else: 79 | heap.decrease_key(neighbor) 80 | 81 | return results 82 | 83 | 84 | if __name__ == '__main__': 85 | testcase = get_input() 86 | result = dijkstra(testcase[0], testcase[1], testcase[2]) 87 | print(*result) 88 | -------------------------------------------------------------------------------- /prim_mst/check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | import sys 23 | from collections import defaultdict 24 | from get_input import get_input # pylint: disable=wrong-import-position # noqa 25 | from node import Node # pylint: disable=wrong-import-position # noqa 26 | from critical_op import compare_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 27 | from prim_mst import prim 28 | 29 | 30 | def check(oracle, to_check) -> bool: 31 | '''检查oracle和to_check是否完全一致. 32 | 33 | Args: 34 | oracle: 给定的正确结果 35 | to_check: 待验证的输入 36 | 37 | Returns: 38 | oracle是否与to_check完全一致的检查结果 39 | ''' 40 | return oracle == to_check 41 | 42 | 43 | def mst_weight_check(node_num: int, edges: list[tuple], to_check: int) -> bool: 44 | '''检查to_check是否为最小权重和, 采用遍历数组找最值的实现方法 45 | 46 | Args: 47 | node_num (int): 图的节点数 48 | edges (list[tuple]): 图所有的边 49 | to_check (int): 待检查的权重和 50 | 51 | Returns: 52 | to_check是否为最小权重和 53 | ''' 54 | max_weight = 0 # 最大的边权重 55 | adjacent_dict = defaultdict(list[Node]) # 注意:defaultdict(list)必须以list做为变量 56 | for node_1, node_2, weight in edges: 57 | max_weight = max(max_weight, weight) 58 | adjacent_dict[node_1].append(Node(node_2, node_1, weight)) 59 | adjacent_dict[node_2].append(Node(node_1, node_2, weight)) 60 | 61 | start = 0 # 以编号为0的节点作为Prim算法起始点 62 | visited = [False for _ in range(node_num)] # 是否访问过该节点 63 | visited[0] = True 64 | oracle = 0 # mst的权重 65 | mst_node = {0} # 当前mst中具有的节点 66 | 67 | # 所有节点的权重均为最大, 保证不被选取 68 | node_weight = [Node(-1, -1, max_weight+1) for _ in range(node_num)] 69 | for neighbor in adjacent_dict[start]: 70 | node_weight[neighbor.node_id] = neighbor 71 | visited[neighbor.node_id] = True 72 | 73 | while len(mst_node) < node_num: 74 | node = min(node_weight) # 遍历找去权重最小的 75 | mst_node.add(node.node_id) # 该节点加入mst 76 | oracle += node.weight # 加入该节点的权重 77 | node_weight[node.node_id].weight = max_weight+1 # 保证不再被选取 78 | for neighbor in adjacent_dict[node.node_id]: 79 | if visited[neighbor.node_id] is False: # 尚未访问过该节点 80 | node_weight[neighbor.node_id] = neighbor 81 | visited[neighbor.node_id] = True 82 | elif neighbor.node_id not in mst_node: # 节点在Fringe中, 考虑更新其权重 83 | if node_weight[neighbor.node_id].weight > neighbor.weight: 84 | node_weight[neighbor.node_id] = neighbor 85 | 86 | return check(oracle, to_check) 87 | 88 | 89 | if __name__ == '__main__': 90 | testcase = get_input() 91 | result = prim(testcase[0], testcase[1]) 92 | if mst_weight_check(testcase[0], testcase[1], result): 93 | print('PASS') 94 | print('Critical op counts:', compare_op.get_op_count()) 95 | else: 96 | print('FAIL') 97 | print('Input :', testcase) 98 | print('Result:', result) 99 | sys.exit(1) 100 | -------------------------------------------------------------------------------- /prim_mst/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from random import randint 23 | from random import choice 24 | import argparse 25 | 26 | 27 | def get_random_input(node_num: int, edge_num: int, min_w: int, max_w: int) \ 28 | -> list: 29 | '''获取随机生成的加权无向连通图 30 | 31 | Args: 32 | node_num(int): 图的节点数 33 | edge_num(int): 图的边数 34 | min_w(int): 边权重最小值 35 | max_w(int): 边权重最大值 36 | 37 | Returns: 38 | 图上所有边 39 | ''' 40 | nodes = list(range(node_num)) 41 | edges = [] 42 | node_pair = set() 43 | 44 | used_nodes = set() 45 | unused_nodes = set(nodes) 46 | 47 | # 先添加n-1条边, 保证图连通 48 | while len(edges) < node_num-1: 49 | if len(edges) == 0: # 生成第一条边 50 | node_1 = randint(0, node_num-2) 51 | node_2 = randint(node_1+1, node_num-1) 52 | used_nodes = used_nodes.union((node_1, node_2)) 53 | unused_nodes = unused_nodes-{node_1, node_2} 54 | edges.append((node_1, node_2, randint(min_w, max_w))) 55 | node_pair.add((node_1, node_2)) 56 | else: # 生成剩余的n-2条边 57 | node_1 = choice(list(used_nodes)) 58 | node_2 = choice(list(unused_nodes)) 59 | used_nodes.add(node_2) 60 | unused_nodes.remove(node_2) 61 | if node_2 < node_1: 62 | node_1, node_2 = node_2, node_1 63 | edges.append((node_1, node_2, randint(min_w, max_w))) 64 | node_pair.add((node_1, node_2)) 65 | 66 | # 添加剩余的m-n+1条边 67 | while len(edges) < edge_num: 68 | node_1 = randint(0, node_num-2) 69 | node_2 = randint(node_1+1, node_num-1) 70 | if (node_1, node_2) not in node_pair: 71 | edges.append((node_1, node_2, randint(min_w, max_w))) 72 | node_pair.add((node_1, node_2)) 73 | 74 | return edges 75 | 76 | 77 | if __name__ == '__main__': 78 | parser = argparse.ArgumentParser(description="Genrate random test inputs") 79 | parser.add_argument('-n', dest='min_node', type=int, default=5, 80 | help="min node number") 81 | parser.add_argument('-N', dest='max_node', type=int, default=100, 82 | help="max node number") 83 | parser.add_argument('-m', dest='min', type=int, default=10, 84 | help="min edge number") 85 | parser.add_argument('-M', dest='max', type=int, default=50000, 86 | help="max edge number") 87 | parser.add_argument('-w', dest='min_w', type=int, default=2, 88 | help="min edge weight") 89 | parser.add_argument('-W', dest='max_w', type=int, default=20, 90 | help="max edge weight") 91 | parser.add_argument(dest='num', type=int, default=10, nargs='?', 92 | help="number of test input") 93 | args = parser.parse_args() 94 | for _ in range(args.num): 95 | node_number = randint(args.min_node, args.max_node) 96 | edge_number = randint(min(args.min, node_number*2-1), 97 | min(args.max, node_number*2-1)) 98 | edge_list = get_random_input( 99 | node_number, edge_number, args.min_w, args.max_w) 100 | print(node_number, len(edge_list)) 101 | for edge in edge_list: 102 | print(' '.join(map(str, edge))) 103 | -------------------------------------------------------------------------------- /prim_mst/min_heap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2023 Ruize Tang , Runze Wu 5 | # . 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from node import Node 23 | from critical_op import compare_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 24 | 25 | 26 | class MinHeap: 27 | '''最小堆 28 | 29 | 最小堆中的元素为我们的节点Node类. 30 | 为了保证decrease_key操作在O(log n)时间完成, 我们维护了节点到堆中对应位置的映射. 31 | ''' 32 | 33 | def __init__(self, node_num: int): 34 | '''堆和位置映射初始化''' 35 | # 采用数组存储堆 36 | self.heap = [] 37 | # 采用数组结构记录节点id对应在堆中数组的下标, 访问存取均为O(1) 38 | self.node_pos = [-1 for _ in range(node_num)] # 下标初始化为-1, 表示尚未加入堆 39 | 40 | def __len__(self): 41 | return len(self.heap) 42 | 43 | def parent(self, index) -> int: 44 | '''对应下标的父节点''' 45 | return (index - 1) // 2 46 | 47 | def left_child(self, index) -> int: 48 | '''对应下标的左子节点''' 49 | return 2 * index + 1 50 | 51 | def right_child(self, index) -> int: 52 | '''对应下标的右子节点''' 53 | return 2 * index + 2 54 | 55 | def get_min(self) -> (Node | None): 56 | '''返回最小堆的堆顶元素''' 57 | if not self.heap: 58 | return None 59 | return self.heap[0] 60 | 61 | def insert(self, node: Node): 62 | '''插入节点node到最小堆中''' 63 | assert self.node_pos[node.node_id] == -1 # 节点之前必须不在堆中 64 | self.heap.append(node) 65 | self.node_pos[node.node_id] = len(self.heap) - 1 66 | self._heapify_up(len(self.heap) - 1) 67 | 68 | def decrease_key(self, node: Node): 69 | '''更新节点node在堆中的权重''' 70 | node_id, weight = node.node_id, node.weight 71 | assert self.node_pos[node.node_id] != -1 # 节点之前必须在堆中 72 | index = self.node_pos[node_id] # 找到节点当前在堆中位置 73 | cur_node = self.heap[index] 74 | assert isinstance(cur_node, Node) and cur_node.node_id == node_id 75 | if compare_op(cur_node.weight, weight): 76 | self.heap[index] = node # 更新节点权重 77 | self._heapify_up(index) # 向上调整堆结构 78 | 79 | def extract_min(self) -> (Node | None): 80 | '''弹出并返回堆顶元素''' 81 | if not self.heap: 82 | return None 83 | 84 | min_node = self.heap[0] 85 | last_node = self.heap.pop() 86 | assert isinstance(min_node, Node) and isinstance(last_node, Node) 87 | if self.heap: 88 | self.heap[0] = last_node 89 | self.node_pos[last_node.node_id] = 0 90 | self._heapify_down(0) 91 | assert self.node_pos[min_node.node_id] != -1 92 | self.node_pos[min_node.node_id] = -1 # 删除被弹出节点的位置映射 93 | return min_node 94 | 95 | def _heapify_up(self, index): 96 | '''向上调整堆''' 97 | while index > 0 and \ 98 | compare_op(self.heap[self.parent(index)], self.heap[index]): 99 | self._swap_node(index, self.parent(index)) 100 | index = self.parent(index) 101 | 102 | def _heapify_down(self, index): 103 | '''向下调整堆''' 104 | min_index = index 105 | left = self.left_child(index) 106 | if left < len(self.heap) and \ 107 | compare_op(self.heap[min_index], self.heap[left]): 108 | min_index = left 109 | right = self.right_child(index) 110 | if right < len(self.heap) and \ 111 | compare_op(self.heap[min_index], self.heap[right]): 112 | min_index = right 113 | if index != min_index: 114 | self._swap_node(index, min_index) 115 | self._heapify_down(min_index) 116 | 117 | def _swap_node(self, a_node_index: int, b_node_index: int): 118 | '''交换堆中的两个节点位置''' 119 | a_node = self.heap[a_node_index] 120 | b_node = self.heap[b_node_index] 121 | assert isinstance(a_node, Node) and isinstance(b_node, Node) 122 | self.heap[a_node_index] = b_node 123 | self.heap[b_node_index] = a_node 124 | # 更新节点及其在数组中对应的下标 125 | self.node_pos[a_node.node_id] = b_node_index 126 | self.node_pos[b_node.node_id] = a_node_index 127 | 128 | 129 | if __name__ == '__main__': 130 | heap = MinHeap(100) 131 | heap.insert(Node(5, 1, 5)) 132 | heap.insert(Node(4, 1, 4)) 133 | heap.insert(Node(3, 1, 3)) 134 | heap.insert(Node(2, 1, 2)) 135 | heap.insert(Node(1, 1, 1)) 136 | print(heap.get_min()) 137 | heap.decrease_key(Node(4, 1, 0)) 138 | print(heap.get_min()) 139 | print(heap.heap) 140 | print(heap.node_pos) 141 | for i in range(5): 142 | print(heap.extract_min()) 143 | -------------------------------------------------------------------------------- /dijkstra/min_heap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com> 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | from node import Node 23 | from critical_op import compare_op # pylint: disable=wrong-import-position, no-name-in-module # noqa 24 | 25 | 26 | class MinHeap: 27 | '''最小堆 28 | 29 | 最小堆中的元素为我们的节点Node类. 30 | 为了保证decrease_key操作在O(log n)时间完成, 我们维护了节点到堆中对应位置的映射. 31 | ''' 32 | 33 | def __init__(self, node_num: int): 34 | '''堆和位置映射初始化''' 35 | # 采用数组存储堆 36 | self.heap = [] 37 | # 采用数组结构记录节点id对应在堆中数组的下标, 访问存取均为O(1) 38 | self.node_pos = [-1 for _ in range(node_num)] # 下标初始化为-1, 表示尚未加入堆 39 | 40 | def __len__(self): 41 | return len(self.heap) 42 | 43 | def parent(self, index) -> int: 44 | '''对应下标的父节点''' 45 | return (index - 1) // 2 46 | 47 | def left_child(self, index) -> int: 48 | '''对应下标的左子节点''' 49 | return 2 * index + 1 50 | 51 | def right_child(self, index) -> int: 52 | '''对应下标的右子节点''' 53 | return 2 * index + 2 54 | 55 | def get_min(self) -> (Node | None): 56 | '''返回最小堆的堆顶元素''' 57 | if not self.heap: 58 | return None 59 | return self.heap[0] 60 | 61 | def insert(self, node: Node): 62 | '''插入节点node到最小堆中''' 63 | assert self.node_pos[node.node_id] == -1 # 节点之前必须不在堆中 64 | self.heap.append(node) 65 | self.node_pos[node.node_id] = len(self.heap) - 1 66 | self._heapify_up(len(self.heap) - 1) 67 | 68 | def decrease_key(self, node: Node): 69 | '''更新节点node在堆中的权重''' 70 | node_id, dist = node.node_id, node.dist 71 | assert self.node_pos[node.node_id] != -1 # 节点之前必须在堆中 72 | index = self.node_pos[node_id] # 找到节点当前在堆中位置 73 | cur_node = self.heap[index] 74 | assert isinstance(cur_node, Node) and cur_node.node_id == node_id 75 | if compare_op(cur_node.dist, dist): 76 | self.heap[index] = node # 更新节点权重 77 | self._heapify_up(index) # 向上调整堆结构 78 | 79 | def extract_min(self) -> (Node | None): 80 | '''弹出并返回堆顶元素''' 81 | if not self.heap: 82 | return None 83 | 84 | min_node = self.heap[0] 85 | last_node = self.heap.pop() 86 | assert isinstance(min_node, Node) and isinstance(last_node, Node) 87 | if self.heap: 88 | self.heap[0] = last_node 89 | self.node_pos[last_node.node_id] = 0 90 | self._heapify_down(0) 91 | assert self.node_pos[min_node.node_id] != -1 92 | self.node_pos[min_node.node_id] = -1 # 删除被弹出节点的位置映射 93 | return min_node 94 | 95 | def _heapify_up(self, index): 96 | '''向上调整堆''' 97 | while index > 0 and \ 98 | compare_op(self.heap[self.parent(index)], self.heap[index]): 99 | self._swap_node(index, self.parent(index)) 100 | index = self.parent(index) 101 | 102 | def _heapify_down(self, index): 103 | '''向下调整堆''' 104 | min_index = index 105 | left = self.left_child(index) 106 | if left < len(self.heap) and \ 107 | compare_op(self.heap[min_index], self.heap[left]): 108 | min_index = left 109 | right = self.right_child(index) 110 | if right < len(self.heap) and \ 111 | compare_op(self.heap[min_index], self.heap[right]): 112 | min_index = right 113 | if index != min_index: 114 | self._swap_node(index, min_index) 115 | self._heapify_down(min_index) 116 | 117 | def _swap_node(self, a_node_index: int, b_node_index: int): 118 | '''交换堆中的两个节点位置''' 119 | a_node = self.heap[a_node_index] 120 | b_node = self.heap[b_node_index] 121 | assert isinstance(a_node, Node) and isinstance(b_node, Node) 122 | self.heap[a_node_index] = b_node 123 | self.heap[b_node_index] = a_node 124 | # 更新节点及其在数组中对应的下标 125 | self.node_pos[a_node.node_id] = b_node_index 126 | self.node_pos[b_node.node_id] = a_node_index 127 | 128 | 129 | if __name__ == '__main__': 130 | heap = MinHeap(100) 131 | heap.insert(Node(5, 1, 5)) 132 | heap.insert(Node(4, 1, 4)) 133 | heap.insert(Node(3, 1, 3)) 134 | heap.insert(Node(2, 1, 2)) 135 | heap.insert(Node(1, 1, 1)) 136 | print(heap.get_min()) 137 | heap.decrease_key(Node(4, 1, 0)) 138 | print(heap.get_min()) 139 | print(heap.heap) 140 | print(heap.node_pos) 141 | for i in range(5): 142 | print(heap.extract_min()) 143 | -------------------------------------------------------------------------------- /dijkstra/generator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright (c) 2024 Ruize Tang , Runze Wu 5 | # , Jingbo Zhai <306330361@qq.com>. 6 | # 7 | # This file is a part of Disalg-ICS-NJU/algocentric. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, version 3. 12 | # 13 | # This program is distributed in the hope that it will be useful, but 14 | # WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | # General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | from random import randint 22 | from random import sample 23 | from random import shuffle 24 | import copy 25 | import argparse 26 | from bellmanford import bellman_ford 27 | 28 | 29 | 30 | def get_random_graph(node_num: int, edge_num: int, min_w: int, max_w: int) \ 31 | -> list: 32 | '''获取随机生成的非负权有向连通图 33 | 34 | Args: 35 | node_num(int): 图的节点数 36 | edge_num(int): 图的边数 37 | min_w(int): 边权重最小值 38 | max_w(int): 边权重最大值 39 | 40 | Returns: 41 | 图的邻接矩阵 42 | ''' 43 | nodes = list(range(node_num)) 44 | edges = [[0 for _ in range(node_num)] for _ in range(node_num)] 45 | edge_size = 0 46 | node_pair = set() 47 | 48 | # 保证图的连通性,先生成一个包含所有节点的随机环 49 | shuffle(nodes) 50 | for i in range(node_num): 51 | node_1 = nodes[i] 52 | node_2 = nodes[(i + 1) % node_num] 53 | weight = randint(min_w, max_w) 54 | edges[node_1][node_2] = weight 55 | node_pair.add((node_1, node_2)) 56 | edge_size += 1 57 | 58 | # 添加剩余的边直到达到edge_num 59 | while edge_size < edge_num: 60 | node_1 = randint(0, node_num - 1) 61 | node_2 = randint(0, node_num - 1) 62 | if node_1 != node_2 and (node_1, node_2) not in node_pair: 63 | weight = randint(min_w, max_w) 64 | edges[node_1][node_2] = weight 65 | node_pair.add((node_1, node_2)) 66 | edge_size += 1 67 | 68 | return edges 69 | 70 | def get_negative_edges(node_num: int,edges: list) \ 71 | -> list: 72 | new_edges = copy.deepcopy(edges) 73 | 74 | for _ in range(randint(1, 20)): 75 | while True: 76 | temp_edges = copy.deepcopy(new_edges) 77 | cycle_nodes = sample(range(0, node_num), 2) 78 | 79 | node_1 = cycle_nodes[0] 80 | node_2 = cycle_nodes[1] 81 | temp_edges[node_1][node_2] = randint(-20, -1) 82 | 83 | result_edgeslist = [] 84 | for i in range(node_num): 85 | for j in range(node_num): 86 | if temp_edges[i][j] != 0: 87 | result_edgeslist.append((i, j, temp_edges[i][j])) 88 | 89 | if bellman_ford(node_num, 0, result_edgeslist) is not None: 90 | new_edges = temp_edges 91 | break 92 | return new_edges 93 | 94 | def get_negative_cycle(node_num: int,edges: list) \ 95 | -> list: 96 | # 随机生成负权环的顶点 97 | cycle_nodes=sample(range(0, node_num), randint(3,node_num)) 98 | node_1=cycle_nodes[-1] 99 | node_2=cycle_nodes[0] 100 | edges[node_1][node_2]=randint(-20,-2) 101 | edges[node_2][node_1]=0 102 | #print(node_1,node_2,edges[node_1][node_2]) 103 | for i in range(len(cycle_nodes) - 1): 104 | node_1=cycle_nodes[i] 105 | node_2=cycle_nodes[i+1] 106 | edges[node_1][node_2]=randint(-20,-2) 107 | edges[node_2][node_1]=0 108 | #print(node_1,node_2,edges[node_1][node_2]) 109 | return edges 110 | 111 | def get_random_input(node_num: int, edge_num: int, 112 | has_negative_edges: bool = False, 113 | has_negative_cycle: bool = False) \ 114 | -> list: 115 | 116 | min_weight=2 117 | max_weight=20 118 | #先生成一个不含负权边的有向连通图 119 | edges = get_random_graph(node_num, edge_num, min_weight, max_weight) 120 | 121 | if has_negative_cycle: 122 | edges= get_negative_cycle(node_num,edges) 123 | elif has_negative_edges: 124 | edges= get_negative_edges(node_num,edges) 125 | 126 | result_edgeslist= [] 127 | for i in range(0,node_num): 128 | for j in range(0,node_num): 129 | if edges[i][j]: 130 | result_edgeslist.append((i,j,edges[i][j])) 131 | return result_edgeslist 132 | 133 | 134 | if __name__ == '__main__': 135 | parser = argparse.ArgumentParser(description="Genrate random test inputs") 136 | parser.add_argument('-n', dest='min_node', type=int, default=5, 137 | help="min node number") 138 | parser.add_argument('-N', dest='max_node', type=int, default=100, 139 | help="max node number") 140 | parser.add_argument('-m', dest='min', type=int, default=10, 141 | help="min edge number") 142 | parser.add_argument('-M', dest='max', type=int, default=500, 143 | help="max edge number") 144 | parser.add_argument(dest='num', type=int, default=1, nargs='?', 145 | help="number of test input") 146 | parser.add_argument("--has_negative_edges", action="store_true", 147 | help="Whether the graph can have negative edges") 148 | parser.add_argument("--has_negative_cycle", action="store_true", 149 | help="Whether the graph can have negative cycles") 150 | 151 | args = parser.parse_args() 152 | for _ in range(args.num): 153 | node_number = randint(args.min_node, args.max_node) 154 | max_edge_number = min(args.min,args.max,node_number*(node_number-1)/2) 155 | min_edge_number = min(args.min,node_number,max_edge_number) 156 | edge_number = randint(min_edge_number,max_edge_number) 157 | edge_list = get_random_input( 158 | node_number, edge_number, args.has_negative_edges, args.has_negative_cycle) 159 | print(node_number, len(edge_list),randint(1,node_number-1)) 160 | for edge in edge_list: 161 | print(' '.join(map(str, edge))) 162 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------