Check in algorithm topic: Ques20201011

 Problem 1 The closest point to the problem

 

(1) One-dimensional

① n nearest point pair problem, q one-dimensional nearest point pair problem, n points on the straight line adopt the sorting + scanning method, and the time complexity is O(nlogn).

Brute force algorithm (not recommended): Pairwise comparison, T(n)=(n-1)(n-2)/2=O(n2).

Divide and conquer

Time complexity is O(nlogn)

The main idea:

Divide a row of points with only x coordinates into two parts (partition in fast row), the number of divisions m=(m1+m2)/2, m1 and m2 are the maximum and minimum values ​​in the array, similar to Digits.

Then divide the two sides to find the point pairs. At this time, you may encounter a situation where the center point m is crossed, and one side is one. The solution is to find the maximum value p of the left array, the minimum value q of the right array, and change the value of qp The value is compared with the two minimum distances found on the left and right.

Fake code:

c++ code:

// 一维

#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <ctime>
using namespace std;
#include <vector>

const int L = 100;
//点对结构体
struct Pair
{
    float d;//点对距离
    float d1, d2;//点对坐标
};
float Random();
int input(vector<float>& s);//构造S
float Max(vector<float>& s, int p, int q);
float Min(vector<float>& s, int p, int q);
template <class Type>
void Swap(Type& x, Type& y);
template <class Type>
int Partition(Type s[], Type x, int l, int r);
Pair Cpair(vector<float>& s, int l, int r);

// 一维 1d
//Ques20200927
int main()
{
    srand((unsigned)time(NULL));
    int m;
	vector<float> s(L);
    Pair d;
    m = input(s); // m是length
    d = Cpair(s, 0, m - 1);
    cout << endl << "最近点对坐标为: (d1:" << d.d1 << ",d2:" << d.d2 << ")";
    cout << endl << "这两点距离为: " << d.d << endl;
    return 0;
}


float Random()
{
    float result = rand() % 10000;
    return result * 0.01;
}

int input(vector<float>& s)
{
    int length;
    cout << "输入点的数目: ";
    cin >> length;
    cout << "点集在X轴上坐标为:";
    for (int i = 0; i < length; i++)
    {
        s[i] = Random();//产生随机数组元素
        cout << s[i] << " ";
    }
    return length;
}


float Max(vector<float>& s, int l, int r)//返回s[]中的最大值
{
    float s_max = s[l];
    for (int i = l + 1; i <= r; i++)
        if (s_max < s[i])
            s_max = s[i];
    return s_max;
}

float Min(vector<float>& s, int l, int r)//返回s[]中的最小值
{
    float s_min = s[l];
    for (int i = l + 1; i <= r; i++)
        if (s_min > s[i])
            s_min = s[i];
    return s_min;
}

template <class Type>
void Swap(Type& x, Type& y)
{
    Type temp = x;
    x = y;
    y = temp;
}


int Partition(vector<float>& s, float x, int l, int r)//快速排序中的一次划分
{
    int i = l - 1, j = r + 1;
    while (true)
    {
        while (s[++i] < x && i < r);
        while (s[--j] > x);
        if (i >= j)
        {
            break;
        }
        Swap(s[i], s[j]);
    }
    return j;
}

//返回s[]中的具有最近距离的点对及其距离
Pair Cpair(vector<float>& s, int l, int r)
{
    Pair min_d = { 99999,0,0 };//最短距离
    if (r - l < 1) return min_d;

    float m1 = Max(s, l, r), m2 = Min(s, l, r);
    float m = (m1 + m2) / 2;//找出点集中的中位数

    //将点集中的各元素按与m的大小关系分组
    int j = Partition(s, m, l, r);

    Pair d1 = Cpair(s, l, j), d2 = Cpair(s, j + 1, r);//递归,求左边最小距离,右边最小距离
    float p = Max(s, l, j), q = Min(s, j + 1, r);//左边最大值,右边最小值

    //返回s[]中的具有最近距离的点对及其距离
    if (d1.d < d2.d) // d1比较小
    {
        if ((q - p) < d1.d)  // 横跨的q - p比d1还要小
        {
            min_d.d = (q - p);
            min_d.d1 = q;
            min_d.d2 = p;
            return min_d;
        }
        else return d1;
    }
    else
    {
        if ((q - p) < d2.d)
        {
            min_d.d = (q - p);
            min_d.d1 = q;
            min_d.d2 = p;
            return min_d;
        }
        else return d2;
    }
}

(2) Two-dimensional point pair

Two-dimensional is more complicated. Divide and conquer is also used to find the nearest point pair of S1 and S2. Use the median of the x-coordinates to divide the point set

 Let d 1 and d 2 be the distance of the closest point pair in S1 and S2 ,

But there is no need to traverse each one like one-dimensional, so the complexity is O(n^2).

Compress the search range of the nearest point pair

Split line L : x=m, m is the median of the x coordinate of each point in S

d = min (d1, d2)

When it’s one side

If the true distance is less than d, there will be one on each side. Assuming p and q points, then the distance between p and q and the line L is less than d. The three-sided relationship of the triangle is easy to see.

pq point relationship

From the meaning of d, the distance between any two points in S in S2 is not less than d. The rectangle Sp shown in the red box in the figure below can be obtained, and it can be deduced that there are only 6 points in S in the rectangle Sp.

 

In fact, we can divide the side of the rectangle Sp whose length is 2d into 3 equal parts, and the side of the rectangle Sp whose length is d into 2 equal parts, thus deriving 6 rectangles of (d/2)×(2d/3) 

Divide Sp into 6 squares according to three equal parts in the vertical direction and 2 parts in the horizontal direction. Each square has at most one point in S2, and the search range contains at most 6 points in S2. 

 

 

For any point p in S1, there are at most 6 points in P2 that constitute the closest point pair candidates.

In the merge step of the divide and conquer method, we only need to check at most 6×n/2=3n pairs of candidates instead of n^2/4 pairs of candidates.

Fake code:

C++ code:

It is divided into an X array in increasing order of x coordinates, a Y array in increasing order of y coordinates, and a Z array (divided by area) used to store all points finally.

When the recursion starts, the exit is when there are only two or three points.

The recursive body starts, m=(left+right)/2

Two pointers point to left and m+1 respectively

For loops from left to right, Z: Record which points are in the left and right areas. Left Z[l:m], right Z[m+1:r], according to the y coordinate from small to large, if its x coordinate>m, divided into the right half area. If its x coordinate<m, divided into The left half of the district.

Recursively find the two nearest points a and b on the left and the distance d

Recursively find the two nearest points ar and br on the right and the distance dr

Compare the minimum distance dr <d from the two sides, and update a, b, d.

Merge(Z, Y, l, m, r);//Reconstruct the array Y, according to the y coordinate size of the point, record it to the Y array

Scan each point in the Y array and place the points in the rectangle in Z,

Then search Z[l:k-1] to find if there is a q point with a smaller distance.

//最接近点对
//二维

//2d10-2 二维最邻近点对问题
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <ctime>
#include<cmath>
using namespace std;

const int M = 50;

//用类PointX和PointY表示依x坐标和y坐标排好序的点
class PointX {
public:
	int operator<=(PointX a)const
	{
		return (x <= a.x);
	}
	int ID; //点编号
	float x, y; //点坐标
};

class PointY {
public:
	int operator<=(PointY a)const
	{
		return(y <= a.y);
	}
	int p; //同一点在数组x中的坐标
	float x, y; //点坐标
};

float Random();
template <class Type>
float dis(const Type& u, const Type& v);

bool Cpair2(PointX X[], int n, PointX& a, PointX& b, float& d);
void closest(PointX X[], PointY Y[], PointY Z[], int l, int r, PointX& a, PointX& b, float& d);

template <typename Type>
void Copy(Type a[], Type b[], int left, int right);

template <class Type>
void Merge(Type c[], Type d[], int l, int m, int r);

template <class Type>
void MergeSort(Type a[], Type b[], int left, int right);

// 二维 2d
int main()
{
	srand((unsigned)time(NULL));
	int length;

	cout << "请输入点对数:";
	cin >> length;

	PointX X[M];
	cout << "随机生成的二维点对为:" << endl;

	for (int i = 0; i < length; i++)
	{
		X[i].ID = i;//点的编号从0开始到length-1
		X[i].x = Random();
		X[i].y = Random();
		cout << "(" << X[i].x << "," << X[i].y << ") ";
	}

	PointX a;
	PointX b;
	float d;

	Cpair2(X, length, a, b, d);

	cout << endl;
	cout << "最邻近点对为:(" << a.x << "," << a.y << ")和(" << b.x << "," << b.y << ") " << endl;
	cout << "最邻近距离为: " << d << endl;

	return 0;
}



//平面上任意两点u和v之间的距离可计算如下
template <class Type>
inline float dis(const Type& u, const Type& v)
{
	float dx = u.x - v.x;
	float dy = u.y - v.y;
	return sqrt(dx * dx + dy * dy);
}

bool Cpair2(PointX X[], int n, PointX& a, PointX& b, float& d)
{//共n个点
	if (n < 2) return false;

	PointX* tmpX = new PointX[n];
	MergeSort(X, tmpX, 0, n - 1);//按点的x坐标从小到大排序

	PointY* Y = new PointY[n];
	for (int i = 0; i < n; i++) //将数组X中的点复制到数组Y中
	{
		Y[i].p = i;
		Y[i].x = X[i].x;
		Y[i].y = X[i].y;
	}

	PointY* tmpY = new PointY[n];
	MergeSort(Y, tmpY, 0, n - 1);//按点的y坐标排序

	PointY* Z = new PointY[n];
	closest(X, Y, Z, 0, n - 1, a, b, d);

	delete[]Y; // 
	delete[]Z;
	delete[]tmpX;
	delete[]tmpY;
	return true;
}
void closest(PointX X[], PointY Y[], PointY Z[], int l, int r, PointX& a, PointX& b, float& d)
{
	if (r - l == 1) //两点的情形
	{
		a = X[l];
		b = X[r];
		d = dis(X[l], X[r]);
		return;
	}

	if (r - l == 2) //3点的情形
	{
		float d1 = dis(X[l], X[l + 1]);
		float d2 = dis(X[l + 1], X[r]);
		float d3 = dis(X[l], X[r]);

		if (d1 <= d2 && d1 <= d3)
		{
			a = X[l];
			b = X[l + 1];
			d = d1;
			return;
		}

		if (d2 <= d3)
		{
			a = X[l + 1];
			b = X[r];
			d = d2;
		}
		else {
			a = X[l];
			b = X[r];
			d = d3;
		}
		return;
	}

	//多于3点的情形,用分治法
	int m = (l + r) / 2;
	int f = l, g = m + 1;

	//在算法预处理阶段,将数组X中的点依x坐标排序,将数组Y中的点依y坐标排序
	//算法分割阶段,将子数组X[l:r]均匀划分成两个不相交的子集,取m=(l+r)/2
	//X[l:m]和X[m+1:r]就是满足要求的分割。
	for (int i = l; i <= r; i++)
	{//Z:记录左右两个区中有哪些点。左Z[l:m],右Z[m+1:r]
		if (Y[i].p > m) Z[g++] = Y[i];//按y坐标从小到大,若它的x坐标>m,分到右半个区
		else Z[f++] = Y[i];//若它的x坐标<m,分到左半个区
	}

	closest(X, Z, Y, l, m, a, b, d);//递归求左边的最近的两个点a和b及距离d

	float dr;
	PointX ar, br;
	closest(X, Z, Y, m + 1, r, ar, br, dr);//递归求右边的最近的两个点ar和br及距离dr

	if (dr < d)
	{
		a = ar;
		b = br;
		d = dr;
	}

	Merge(Z, Y, l, m, r);//重构数组Y,按点的y坐标大小,记录到Y数组

	//d矩形条内的点置于Z中
	int k = l;
	for (int i = l; i <= r; i++)//扫描Y数组中每一个点
	{
		if (fabs(X[m].x - Y[i].x) < d)//2d宽
		{
			Z[k++] = Y[i];
		}
	}

	//搜索Z[l:k-1]
	for (int i = l; i < k; i++)
	{
		for (int j = i + 1; j < k && Z[j].y - Z[i].y < d; j++)//高2d矩形框
		{
			float dp = dis(Z[i], Z[j]);
			if (dp < d)
			{
				d = dp;
				a = X[Z[i].p];
				b = X[Z[j].p];
			}
		}
	}
}

template <class Type>
void Merge(Type c[], Type d[], int l, int m, int r)
{
	int i = l, j = m + 1, k = l;
	while ((i <= m) && (j <= r))
	{
		if (c[i] <= c[j])
		{
			d[k++] = c[i++];
		}
		else
		{
			d[k++] = c[j++];
		}
	}

	if (i > m)
	{
		for (int q = j; q <= r; q++)
		{
			d[k++] = c[q];
		}
	}
	else
	{
		for (int q = i; q <= m; q++)
		{
			d[k++] = c[q];
		}
	}
}

template <class Type>
void MergeSort(Type a[], Type b[], int left, int right)
{
	if (left < right)
	{
		int i = (left + right) / 2;
		MergeSort(a, b, left, i);
		MergeSort(a, b, i + 1, right);
		Merge(a, b, left, i, right);//合并到数组b
		Copy(a, b, left, right);//复制回数组a
	}
}

template <typename Type>
void Copy(Type a[], Type b[], int left, int right)
{
	for (int i = left; i <= right; i++)
		a[i] = b[i];
}

Time complexity analysis

Source: PPT from Teacher L, National University of Science and Technology

 

 Problem 2 Fast Fourier Transform

 

Some definitions

 

 (1) Brute force method

 the complexity

Computational complexity: 5 sentences are multiplied by i times, 4 sentence loops are 1+2+…+(2n-1)=2n(2n-1)/2 times, 2 sentences loop 2n times, so T(n)= O(n 3 )

(2) Improve Fourier transform

To avoid double calculations, consider the following algebraic transformations:

 Just calculate for each order .

the complexity

(3) Divide and conquer

Define the following two polynomials of degree N: consisting of a(x) even and odd coefficients respectively

the complexity

According to the main theorem 2, or direct deduction, T(n)=O(NlogN)

Fake code

C++ code

// 快速傅里叶变换

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<complex>
using namespace std;

#define cp complex<double>
#define ll long long
#define PI acos(-1.0)
#define MAXN 4000010

cp a[MAXN], b[MAXN], c[MAXN];
int n, m, lim;

inline ll read() {
    ll s = 0, w = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
    for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
    return s * w;
}

cp omega(int n, int k) {
    return cp{ cos(2 * PI * k / n), sin(2 * PI * k / n) };
}
void fft(cp* a, int n, bool inv) {
    if (n == 1) return;
    static cp buf[MAXN];
    int m = n / 2;
    for (register int i = 0; i < m; i++) {
        buf[i] = a[2 * i];
        buf[i + m] = a[2 * i + 1];
    }
    for (register int i = 0; i < n; i++)
        a[i] = buf[i];
    fft(a, m, inv);
    fft(a + m, m, inv);
    for (register int i = 0; i < m; i++) {
        cp x = omega(n, i);
        if (inv) x = conj(x);
        buf[i] = a[i] + x * a[i + m];
        buf[i + m] = a[i] - x * a[i + m];
    }
    for (register int i = 0; i < n; i++)
        a[i] = buf[i];
}
// Ques20201010
int main() {
    n = read(), m = read();
    // n是 m是
    for (register int i = 0; i <= n; i++)
        a[i] = { (double)read(), 0 };
    for (register int i = 0; i <= m; i++)
        b[i] = { (double)read(), 0 };
    int lim = 1;
    while (lim <= n + m) lim *= 2;
    for (int i = n + 1; i <= lim; i++) a[i] = { 0, 0 };
    for (int i = m + 1; i <= lim; i++) b[i] = { 0, 0 };
    fft(a, lim, true), fft(b, lim, true);
    for (register int i = 0; i <= lim; i++)
        c[i] = a[i] * b[i];
    fft(c, lim, false);
    for (register int i = 0; i <= n + m; i++)
        printf("%d ", (int)((c[i].real() / lim) + 0.5));
    return 0;
}

 

Guess you like

Origin blog.csdn.net/Toky_min/article/details/108997357