小伙伴们大家好!这篇文章记录了我接单写的一个程序,客户要求输入50(以内)个数,输入选取数(20以内),显示他们的排列组合,并且要有一个前端界面。看到这个需求的时候我也没多想,觉得很简单,就接了,写这个程序大概用了3个多小时。当我写完看测试结果的时候,我一度以为被客户耍了,因为如果是50选20的话,这个结果数太大了,一天一夜也算不出来呀。好在最后成果交易,客户肯定是学生,大概是老师出的题目吧(不太明白老师为啥出这种题目)。
一、知识点
我的程序主要用到了三个模块:tkinter(写前端界面),combinations(求组合数),openpyxl(将结果导出到excel)
二、前端编写
通常情况我都是先写程序的核心代码,即先实现功能,再写界面,但是因为实现组合数的功能,可以使用itertools模块中的combinnations轻松实现,所以我先写了前端部分。
按照客户的需求,制定了下面的界面,代码也没什么好多说的。
用户可以输入最多50个数字,选取数最大为20,点击显示结果,出现组合总数、计算时间、还有所有的组合列表,每次显示1000个,再点击显示结果再出现1000个。如果输入的数字或者选取数发生变化的话,点击显示结果就会重新计算组合。点击导出文件会将显示的结果导出到excel文件里。
对于每次点击显示结果会刷新1000个结果这个功能,我定义了一个变量button_num,初始值为0,每点击一次+1,每次点击按钮的时候都判断一下button_num的值,如果等于0就重新计算排列组合,如果大于0就刷新结果,当结果显示完button_num值再赋0。
三、排列组合功能的实现
排列组合我们首先会想到用递归函数去写,但怎么去写呢,递归真的是会者不难,难者不会呀,好在Python具有强大的库,可以轻松帮我们实现。
from itertools import combinations
result = combinations(list[], m)) #list[]为输入的数字列表,m为选取数
for i in result:
print(i)
我没有少写,四行代码就实现了排列组合。首先导入combinations模块,传入输入的50个数字列表和选取数,然后就输出结果了。是不是超级简单,Python真的是yyds。
因为组合数太多了,用列表存放的话会大量占用内存,所以使用了迭代器。
组合数 = sum(1 for _ in combinations(n, m))
所有的排列组合 = iter(i for i in combinations(n, m))
对于combinnations的使用我也是第一次,不太确定他是不是迭代器,只知道迭代一次之后值就消失了,知道的小伙伴可以留言告诉我。在实际的代码中,我调用了两次combinations,第一次是为了计算组合数,第二次是将排列组合结果存放到迭代器中,这是因为我没有找到combinations类似于next()的用法,所以只能把结果放入iter定义的迭代器中,以便后续显示结果。
四、将结果导出到excel
import openpyxl
wb = openpyxl.Workbook()
ws = wb.active
for each in result:
ws.append(each)
wb.save(filename)
操作excel学会两个模块就可以了,一个是pandas,另一个就是openpyxl,这两个模块是互补的,想详细学习的小伙伴可以自行去学习。
五、总结
总体来说这个程序是比较简单的,我挣得也不多,但是作为python的初学者,每一次接单都是对我学习的激励。我也会慢慢在csdn上把我接单的程序发布,既是作为我自己的总结,也是为了能和其他的伙伴进行交流。下面把程序的源码附上。
from itertools import combinations
import re
from tkinter import *
from tkinter import messagebox
import time
import threading
from tkinter.scrolledtext import ScrolledText
import openpyxl
class Application(Frame):
def __init__(self, master):
super().__init__(master)
self.numbers = StringVar()
self.max_num = IntVar()
self.input_n = ''
self.input_m = 0
self.sums = IntVar()
self.times = StringVar()
self.text = Text()
self.result = iter(range(2))
self.button_num = 0
self.show_list = []
self.num = []
self.create_widget()
def create_widget(self):
Label(self.master, text='请输入数字:').place(x=10, y=20)
Entry(self.master, textvariable=self.numbers, width=40).place(x=92, y=20)
Label(self.master, text='选取:').place(x=455, y=20)
Entry(self.master, textvariable=self.max_num, width=4).place(x=495, y=20)
Label(self.master, text='的所有组合').place(x=520, y=20)
Label(self.master, text='组合总数:').place(x=605, y=20)
Label(self.master, textvariable=self.sums).place(x=680, y=20)
Button(self.master, text='显示结果', width=8, height=1, command=lambda: th(self.main)).place(x=760, y=15)
Button(self.master, text='导出文件', width=8, height=1, command=lambda: th(self.save)).place(x=760, y=50)
Label(self.master, text='显示所有组合列表').place(x=10, y=55)
Label(self.master, text='计算时间:').place(x=350, y=55)
Label(self.master, textvariable=self.times).place(x=460, y=55)
self.text = ScrolledText(self.master, width=90, height=18)
self.text.place(x=10, y=90)
def main(self):
start_time = time.time()
if self.input_n != self.numbers.get() or self.input_m != self.max_num.get():
self.button_num = 0
if self.button_num > 0:
self.show()
self.button_num += 1
return
n = []
self.input_n = self.numbers.get()
self.input_m = self.max_num.get()
n_list = re.findall(r'\d+', self.input_n)
if len(n_list) == 0:
messagebox.showinfo(title='提示', message=f'输入为空')
return
if self.input_m > len(n_list):
messagebox.showinfo(title='提示', message=f'输入的选取数大于输入的数字总数')
return
if self.input_m > 20:
messagebox.showinfo(title='提示', message=f'选取数不能大于20')
return
if len(n_list) > 50:
messagebox.showinfo(title='提示', message=f'输入的数字总数不能大于50个')
return
for i in n_list:
n.append(int(i))
self.text.delete('1.0', END)
self.text.insert(END, '计算中...')
self.sums.set(sum(1 for _ in combinations(n, self.input_m)))
self.result = iter(i for i in combinations(n, self.input_m))
end_time = time.time()
self.times.set(str(end_time - start_time))
self.show()
self.button_num += 1
return
def show(self):
self.text.delete('1.0', END)
self.show_list = []
self.num = []
for i in range(1000):
try:
self.show_list.append(self.result.__next__())
self.num.append(self.button_num * 1000 + i + 1)
self.text.insert(END, f'{self.num[-1]} {self.show_list[-1]}\n')
except StopIteration:
self.text.insert(END, '结果显示完毕')
self.button_num = -1
return
return
def save(self):
if not self.show_list:
messagebox.showinfo(title='提示', message=f'结果为空')
return
wb = openpyxl.Workbook()
ws = wb.active
ws.append(['序号', '结果'])
n = 0
for each in self.show_list:
ws.append([self.num[n], str(each)])
n = n + 1
filename = '导出结果(' + str(self.num[0]) + '-' + str(self.num[-1]) + ').xlsx'
wb.save(filename)
messagebox.showinfo(title='提示', message=f'导出成功')
def th(func, *args):
t = threading.Thread(target=func, args=args)
t.setDaemon(True)
t.start()
if __name__ == '__main__':
root = Tk()
root.title('N选M个数组合')
root.geometry('850x410+200+50')
app = Application(root)
root.mainloop()