Algorithm with O(logn) time complexity

I was asked about the time complexity today. I was really confused about O(logn), so I found a blog to learn and record it.
The following content is reproduced from https://blog.csdn.net/u011619283/article/details/51878201

Typical time complexity

We know the execution efficiency of the algorithm, and we can infer one or two from its time complexity. And what are the typical types of time complexity?
Typical time complexity.png
From the above figure, it can be seen that in addition to the constant time complexity, the logN-type algorithm is the most efficient. Today, I will introduce three very easy logN-type algorithms.

binary search

Given an integer X and integers A0, A1, ..., An-1, the latter already pre-sorted and in memory, seek the following table i for Ai = X, and return i = -1 if X is not in the data .

- (int)BinarySearch:(NSArray *)originArray element:(int)element
{
    int low, mid, high;
    low = 0; high = (int)originArray.count - 1;
    while (low <= high) {
        mid = (low + high) / 2;
        if ([originArray[mid] intValue] < element) {
            low = mid + 1;
        } else if ([originArray[mid] intValue] > element) {
            high = mid -1;
        } else {
            return mid;
        }
    }
    
    return -1;
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

* Analysis: * It can be seen from the above program that to calculate the time complexity, it is to find the number of while loops.
The value of mid each time is the array length (N-1)/2, (N-1)/2/2, (N-1)/2/2/2,..., 1, 0, -1. Then just find out how many powers of 2 are equal to N-1, plus the possible number of times 2 needed. Assuming that 2 raised to the f power is equal to N-1, the maximum time is log(N-1) + 2. Therefore, the time complexity of binary search is logN. Another practical example, assuming the initial high = 128, low = 0, the maximum value of mid is 64, 32, 16, 8, 4, 2, 1, 0, -1. Everyone can count time.

Euclidean algorithm

The second is the Euclidean algorithm for calculating the greatest common factor. The greatest common factor Gcd of two integers is the largest integer that divides both of them at the same time. So, Gcd(50,15) = 5.

- (unsigned int)Gcd:(unsigned int)m n:(unsigned int)n
{
    unsigned int Rem;
    while (n > 0) {
        Rem = m % n;
        m = n;
        n = Rem;
    }
    return m;
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

The algorithm is super simple, but there is still some essence in it. The algorithm assumes m>=n, but if m < n, after the first while loop, m and n will be swapped with each other.
The overall running time of the algorithm depends on determining the length of the remainder sequence, which is the number of while loops.
Still for example m = 1989 and n = 1590, then the remainder sequence is 399, 393, 6, 3, 0. Thus, Gcd(1989,1590) = 3.
Although it can't be seen that the value of the remainder is decremented according to the constant primer, sometimes the decrement is very small, such as from 399 to 393. However, we can show that after two iterations, the remainder is at most half the original value. The number of iterations is at most 2logN, so the time complexity is logN.
How to prove that M > N, then M mod N < M /2?
If N =< M/2, then since the remainder is less than N, that is, M mod N < N <= M/2, the remainder is also less than M/2.
If N > M/2, then there is an N in M ​​at this time, so the remainder MN < M/2.

exponentiation

The last algorithm is to calculate the power of an integer. We can use the number of multiplications as a measure of running time.
A common algorithm for computing X to the Nth power is to multiply by N-1 times. But it's better to use a recursive algorithm.

- (long)Pow:(long)x n:(unsigned int)n
{
    if (n == 0) {
        return 1;
    }
    if (n == 1) {
        return x;
    }
    
    if ([self isEven:n]) {
        return [self Pow:x * x n:n / 2];
    } else {
        return [self Pow:x * x n:n / 2] * x;
    }
}

- (BOOL)isEven:(unsigned int)n
{
    if (n % 2 == 0) {
        return YES;
    } else {
        return NO;
    }
}
    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

If N is even, then X to the N power = X to the N/2 power times X to the N/2 power, if N is odd, then X to the N power = X's (N-1)/2 The power multiplied by X to the (N-1)/2 power multiplied by X.
Obviously, the number of multiplications required is at most 2logN. Then the time complexity is logN.

Typical time complexity

We know the execution efficiency of the algorithm, and we can infer one or two from its time complexity. And what are the typical types of time complexity?
Typical time complexity.png
From the above figure, it can be seen that in addition to the constant time complexity, the logN-type algorithm is the most efficient. Today, I will introduce three very easy logN-type algorithms.

binary search

Given an integer X and integers A0, A1, ..., An-1, the latter already pre-sorted and in memory, seek the following table i for Ai = X, and return i = -1 if X is not in the data .

- (int)BinarySearch:(NSArray *)originArray element:(int)element
{
    int low, mid, high;
    low = 0; high = (int)originArray.count - 1;
    while (low <= high) {
        mid = (low + high) / 2;
        if ([originArray[mid] intValue] < element) {
            low = mid + 1;
        } else if ([originArray[mid] intValue] > element) {
            high = mid -1;
        } else {
            return mid;
        }
    }
    
    return -1;
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

* Analysis: * It can be seen from the above program that to calculate the time complexity, it is to find the number of while loops.
The value of mid each time is the array length (N-1)/2, (N-1)/2/2, (N-1)/2/2/2,..., 1, 0, -1. Then just find out how many powers of 2 are equal to N-1, plus the possible number of times 2 needed. Assuming that 2 raised to the f power is equal to N-1, the maximum time is log(N-1) + 2. Therefore, the time complexity of binary search is logN. Another practical example, assuming the initial high = 128, low = 0, the maximum value of mid is 64, 32, 16, 8, 4, 2, 1, 0, -1. Everyone can count time.

Euclidean algorithm

The second is the Euclidean algorithm for calculating the greatest common factor. The greatest common factor Gcd of two integers is the largest integer that divides both of them at the same time. So, Gcd(50,15) = 5.

- (unsigned int)Gcd:(unsigned int)m n:(unsigned int)n
{
    unsigned int Rem;
    while (n > 0) {
        Rem = m % n;
        m = n;
        n = Rem;
    }
    return m;
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

The algorithm is super simple, but there is still some essence in it. The algorithm assumes m>=n, but if m < n, after the first while loop, m and n will be swapped with each other.
The overall running time of the algorithm depends on determining the length of the remainder sequence, which is the number of while loops.
Still for example m = 1989 and n = 1590, then the remainder sequence is 399, 393, 6, 3, 0. Thus, Gcd(1989,1590) = 3.
Although it can't be seen that the value of the remainder is decremented according to the constant primer, sometimes the decrement is very small, such as from 399 to 393. However, we can show that after two iterations, the remainder is at most half the original value. The number of iterations is at most 2logN, so the time complexity is logN.
How to prove that M > N, then M mod N < M /2?
If N =< M/2, then since the remainder is less than N, that is, M mod N < N <= M/2, the remainder is also less than M/2.
If N > M/2, then there is an N in M ​​at this time, so the remainder MN < M/2.

exponentiation

The last algorithm is to calculate the power of an integer. We can use the number of multiplications as a measure of running time.
A common algorithm for computing X to the Nth power is to multiply by N-1 times. But it's better to use a recursive algorithm.

- (long)Pow:(long)x n:(unsigned int)n
{
    if (n == 0) {
        return 1;
    }
    if (n == 1) {
        return x;
    }
    
    if ([self isEven:n]) {
        return [self Pow:x * x n:n / 2];
    } else {
        return [self Pow:x * x n:n / 2] * x;
    }
}

- (BOOL)isEven:(unsigned int)n
{
    if (n % 2 == 0) {
        return YES;
    } else {
        return NO;
    }
}
  
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

If N is even, then X to the N power = X to the N/2 power times X to the N/2 power, if N is odd, then X to the N power = X's (N-1)/2 The power multiplied by X to the (N-1)/2 power multiplied by X.
Obviously, the number of multiplications required is at most 2logN. Then the time complexity is logN.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324731843&siteId=291194637