今天想写写关于蛮力法的一些问题,也给之后自己留下一个笔记。
蛮力法关键------依次处理所有元素
1.查找问题中的蛮力法
- 顺序查找
int SeqSearch(int r[],int n,int k)
{
i=n;
while(i>0&&r[i]!=k)
i--;
return i;
}
上述算法基本语句是i>0和r[i]!=k,其执行次数为:
O(n)
改进的顺序查找
将带查找值放在查找方向的尽头处,免去在查找过程中每一次比较后都要判断查找位置是否越界,从而提高查找速度。
int SeqSearch(int r[],int n,int k)
{
r[0]=k;i=n;
while(r[i]!=k)
i--;
return i;
}
此算法的基本语句是r[i]!=k,其执行次数为:(n+1)/2
O(n)数量级相同,系数相差一半
- 串匹配问题
//文本 T[0...n-1]长度为n
//模式P[0....m-1]长度为m
//查找不成功返回-1
for(int i=0;i<=n-m;i++){
j=0;
while(j<m&&P[j]==T[i+j])
j++;
if(i==m)
return i;
return -1;
}
考虑最坏的情况,每趟不成功匹配都发生在串T的最后一个字符,在(i-1)趟不成功的匹配中共比较了(i-1)m次,第i趟成功的匹配共比较了m次,所以总共比较了im 次,因此平均比较次数是:m(n-m+2)/2
2.排序问题中的蛮力法
- 选择排序
void SelectSort(int r[],int n){
for(int i=0;i<n-1;i++){
index=i;
for(j=i+1;j<=n;j++)
if(r[j]<r[index])
index=j;
if(index!=i)
r[i]------r[index] //交换这两的元素位置
}
}
基本语句是内层循环体中的比较语句r[j]<r[index],执行次数n(n-1)/2,O(n^2)
3. 冒泡排序
void Sort(int r[],int n){
for(i=1;i<=n-1;i++){
for(j=1;j<=n-i;j++)
if(r[j]>r[j+1])
r[j]----r[j+1] //交换元素
}
}
组合问题中的蛮力法
- 生成排列对象
用蛮力法生成{1,2…,n}的所有n!个排列。
例:
开始 1
插入 2 12 21
插入3 123 132 312 213 231 321
//伪代码:
//先生成初始排列{1};
for(i=2;i<=n;i++)
for(j=1;j<=(i-1)!;j++)
for(k=i;k>=1;k--)
将i插入到第j个排列中的第k个位置;
借鉴代码https://blog.csdn.net/wlk1229/article/details/7596837
/*蛮力法解决全排列问题*/
#include<iostream>
#include<string>
using namespace std;
void insert(char c1[], char c2[], char c, int i)
//将c 插入c1 并将c1复制到c2
{
int j;
for (j = 0; j < i; j++)
c2[j] = c1[j];
c2[i] = c;
for (j = i + 1; j <= strlen(c1) + 1; j++)
c2[j] = c1[j - 1];
}
void perml(int n)//为生成排列集合元素的个数
{
int i, j, k, m = 1;
char **c[2];//采用两个字符序列存储,分别存储i-1和i的全排列
c[1] = new char*[1];
c[1][0] = new char[2];
c[1][0][0] = '1'; c[1][0][1] = 0;
for (i = 2; i <= n; i++)
{
m = m*(i - 1);
c[i % 2] = new char*[m*i];
for (j = 0; j < m; j++)
{
for (k = 0; k < i; k++)
{
c[i % 2][j*i + k] = new char[i + 1];
insert(c[(i + 1) % 2][j], c[i % 2][j*i + k], char(i + '0'), k);
}
delete[] c[(i + 1) % 2][j];
}
delete[] c[(i + 1) % 2];
}
for (i = 0; i < m*n; i++)
{
cout << "第" << i + 1 << "排列:" << c[n % 2][i] << endl;
delete[] c[n % 2][i];
}
}
int main()
{
perml(3);
return 0;
}
- 生成子集
字典序
list_1=[]
list_2=[]
answer=[]
n=int(input("input:"))
for i in range(0,n):
list_1.append(input("input:"))
list_2.append(0)
sum1=0
i=n-1
print(answer)
while sum1<n:
i=n-1
if list_2[i]==0:
list_2[i]=1
else:
list_2[i]=0
while i>0:
if list_2[i-1]==1:
list_2[i-1]=0
i=i-1
else:
list_2[i-1]=1
break
sum1=sum(list_2)
for i in range(0,n):
if list_2[i]==1:
answer.append(list_1[i])
print(answer)
answer.clear()
- 0/1背包问题
用蛮力法解决0/1背包问题,需要考虑给定的n个物品集合的所有子集,找出所有可能的子集,计算每个子集的总价值,然后在他们中找到价值最大的子集。对于一个具有n个元度的集合,其子集的数量是2^n,所以无论生成子集的算法效率有多高,蛮力法都会导致一个2的n次方的算法。
自己写的有点糙,还是根据上面那个代码改进着来的
list_weight=[]
list_vlaue=[]
list_2=[]
maxweight=int(input("输入背包最大容量:"))
n=int(input("input n:"))
for i in range(0,n):
list_weight.append(int(input("输入第"+str(i+1)+"件物品的重量:")))
list_vlaue.append(int(input("输入第"+str(i+1)+"件物品的价值:")))
list_2.append(0)
sum1=0
sumw=0
sumv=0
i=n-1
while sum1<n:
i=n-1
sw=0
sv=0
if list_2[i]==0:
list_2[i]=1
else:
list_2[i]=0
while i>0:
if list_2[i-1]==1:
list_2[i-1]=0
i=i-1
else:
list_2[i-1]=1
break
sum1=sum(list_2)
for i in range(0,n):
if list_2[i]==1:
sw=sw+list_weight[i]
sv=sv+list_vlaue[i]
if sw<maxweight and sv>sumv:
sumw=sw
sumv=sv
print(sumv)
- 任务分配问题
假设有n个任务需要分配给n个人执行,每个任务只分配给一个人,每个人只分配一个任务,且第j个任务分配给第i个人的成本是C [ i, j ]1<=i,j<=n),任务分配问题要求找出总成本最小的分配方案。
图问题中的蛮力法
-
哈密顿回路问题
对给定的无向图G=(V,E),首先生成图中所有的顶点的排列对象(vi1,vi2,)然后依次考察每个排列对象是否满足一下两个条件:1.相邻顶点之间存在边;2.最后一个顶点和第一个顶点之间存在边 满足这俩条件的回路就是哈密顿回路。
考察所有点的排列对象,O(n!) -
TSP问题
旅行家要旅行n个城市然后回到出发城市,要求各个城市经历且经历一次,并要求所走的路程最短。用蛮力法解决,找出所有可能的旅行路线,从中选取路径长度最短的简单回路。
几何问题中的蛮力法
- 最近对问题
最近对问题要求找出一个包含n个点的集合中距离最近的两个点;蛮力法求解是分别计算呢每一对点之间的距离,然后找出距离最小的那一对,为了避免对同一对点计算两次距离,只考虑i<j的那些点对(pi,pj)
大致思路如下 O(n^2)
int ClosesPoints(int n,int x=[],int y[],int &index1,int &index2){
minDist=无穷大
for(int i=1;i<n;i++)
for(int j=i+1;j<n;j++)
{
d=(x[i]-x[j])^2+(y[i]-y[j])^2;
if(d<minDist)
{ minDist=d;
index1=i;
index2=j;
}
}
return minDist;
}
- 凸包问题
一个点集S的凸包是包含S的最小凸集合,其中,最小是指S的凸包一定是所有包含S的凸集合的子集,对于平面上的n个点的集合S,他的凸包就是包含所有这些点的最小凸多边形。蛮力法解决凸包问题的基本思想:对于一个由n 个点构成的集合S中的两个点P1和P2,当且仅当该集合中的其他点都位于穿过这两点的直线的同一边时,他们的连线就是该集合凸包边界的一部分。对每一对顶点都检验一遍之后,免租条件的线段构成了该凸包的边界。
在平面上 穿过两点的直线是 ax+by=c (a=y2-y1,b=x1-x2,c=x1y2-x2y1)
这条线吧平面分成零分半平面,其中一个的点满足 ax+by>c
另一个ax+by<c
时间复杂性是O(n的 三次方)
步骤:
将点集里面的所有点两两配对,组成 n(n-1)/2 条直线。
对于每条直线,再检查剩余的 (n-2) 个点是否在直线的同一侧。
如何判断一个点 p3 是在直线 p1p2 的左边还是右边呢?(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3)) ???
https://blog.csdn.net/u011001084/article/details/72768075
借鉴某一大佬写的可视化版本凸包问题
import random
import matplotlib.pyplot as p
input = int(input('输入生成点的数量:'))
dot = [[0] * 3 for i in range(input)]
x = [[0] * 2 for a in range(int(input * (input - 1) / 2))]
y = [[0] * 2 for b in range(int(input * (input - 1) / 2))]
fg = p.figure()
cn = fg.add_subplot(1, 1, 1)
cn.set_xlim(0, 1000)
cn.set_ylim(0, 1000)
p.ion()
for i in range(input):
dot[i][0] = random.randrange(1000)
dot[i][1] = random.randrange(1000)
dot[i][2] = 0
def judge(inp):
n = 0
for i in range(inp):
for j in range(i + 1, inp):
a = dot[j][1] - dot[i][1]
b = dot[i][0] - dot[j][0]
c = (dot[i][0] * dot[j][1]) - (dot[i][1] * dot[j][0])
sign1 = 0
sign2 = 0
x[n][0] = dot[i][0]
x[n][1] = dot[j][0]
y[n][0] = dot[i][1]
y[n][1] = dot[j][1]
n += 1
for k in range(inp):
if k == j or k == i:
continue
if a * dot[k][0] + b * dot[k][1] == c:
sign1 += 1
sign2 += 1
if a * dot[k][0] + b * dot[k][1] > c:
sign1 += 1
if a * dot[k][0] + b * dot[k][1] < c:
sign2 += 1
if (sign1 == (inp - 2)) or (sign2 == (inp - 2)):
dot[i][2] = 1
dot[j][2] = 1
cn.scatter(dot[i][0], dot[i][1], color='g', marker='.')
cn.scatter(dot[j][0], dot[j][1], color='g', marker='.')
cn.plot(x[n - 1], y[n - 1], color='b')
cn.scatter(dot[i][0], dot[i][1], color='g', marker='.')
cn.scatter(dot[j][0], dot[j][1], color='g', marker='.')
cn.plot(x[n - 1], y[n - 1], color='r')
p.pause(0.1)
cn.lines.pop()
judge(input)
print("凸包极点:")
for i in range(input):
if dot[i][2] == 1:
print((dot[i][0], dot[i][1]))