【算法导论 第9章 中位数和顺序统计学】
2017-02-22 07:11
357 查看
第9章 中位数和顺序统计学
1. 第i个顺序统计量是该集合中第i小的元素。 最小值是第1个顺序统计量(i=1)最大值是第n个顺序统计量(i=n)
2. 中位数是它所在集合的“中点元素”,n为奇数时为(n+1)/2,n为偶数时有两个。
3. 找最大最小值的算法,一般人可能以为需要2(n-1)次比较,实际上只需要最多3⌊n/2⌋次比较,使用的技巧是: 将一对元素比较,然后把较大者于max比较,较小者与min比较,因此每两个元素需要比较3次。
如何设定当前最小值和最大值取决于n是偶数还是奇数。如果n为奇数,就将最小值和最大值都设置为第一个元素,然后成对的处理余下元素。如果n是偶数,就对前两个元素做一次比较,已决定最大值和最小值的初值。然后如同n是奇数一样,成对处理余下元素。
比较次数, n为奇数:3⌊n/2⌋ n为偶数:3(n - 2)/ 2 + 1 = 3n/2 - 2
4.以期望线性时间选择顺序统计量的方法是以快速排序为模型。如同在快速排序中一样,此算法的思想也是对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。 这就是顺序统计量算法能够如此高效的核心原因所在!
//////////////////////////////////////////////////////////////////////////
/// @file nth_element.cpp
/// @brief 中位数和顺序统计学
/// @details COPYRIGHT NOTICE
/// Copyright (c) 2011
/// All rights reserved.\n
/// 在O(n)的时间内寻找一个数组中的第i个顺序统计量\n
/// 第i个顺序统计量的定义为:该集合中第i小的元素\n
/// 以期望线性时间选择顺序统计量的方法是以快速排序为模型。如同在快速排序中一样,此算法的思想也是
/// 对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select
/// 只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。
///
//////////////////////////////////////////////////////////////////////////
#include <time.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
namespace ita
{
namespace
{
/// 寻找v数组的子集[begin_index, end_index]中的第i个元素顺序统计量,0 <= i < end_index-begin_index
int _NthElement( vector<int> &v, int const begin_index, int const end_index, int const n )
{
//这个判断纯粹只是一个加速return的技巧,没有这个判断算法也是正确的!
if ( begin_index == end_index )
{
return v[begin_index];
}
//随机取样
int swap_index = rand() % ( end_index - begin_index + 1 ) + begin_index;
swap( v[swap_index], v[end_index] );
//根据最后一个主元进行分割成两部分
int i = begin_index;
for ( int j = begin_index; j < end_index; ++j )
{
if ( v[j] < v[end_index] )
{
swap( v[i++], v[j] );
}
}
swap( v[i], v[end_index] );
//主元是本区间的第k个元素顺序统计量,0<=k<size
int k = i - begin_index;
if ( n == k )
{
//找到了
return v[i];
}
if ( n < k )
{
//在左区间继续找
return _NthElement( v, begin_index, i - 1, n );
}
else
{
//在右区间继续找:由于主元是第k个元素顺序统计量(0<=k<size),所以小于等于主元的元素有k+1个(包括主元),因此寻找右区间的第n-(k+1)个顺序统计量
return _NthElement( v, i + 1, end_index, n - k - 1 );
}
}
}
/// @brief 寻找v数组中的第i个顺序统计量,0<=i<size
///
/// 以快速排序为模型。如同在快速排序中一样,此算法的思想也是
/// 对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select
/// 只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。
/// @param v 要进行查找操作的集合
/// @param i 查找集合中的第i个顺序统计量
/// @return 集合中的第i个顺序统计量
/// @see int _NthElement(vector<int> &v, int const begin_index, int const end_index, int const n)
int NthElement( vector<int> &v, int const i )
{
return _NthElement( v, 0, v.size() - 1, i );
}
/// 中位数和顺序统计学
int testNthElement()
{
vector<int> v;
srand((int)time(0));
for ( int i = 0; i < 10; ++i )
{
v.push_back( ( rand() % 1000 ) );
}
copy( v.begin(), v.end(), ostream_iterator<int>( cout, " " ) );
cout << endl;
for ( int i = 0; i < 10; ++i )
{
cout << i << "th element is:" << NthElement( v, i ) << endl;
}
return 0;
}
}
using namespace ita;
int main()
{
testNthElement();
}
1. 第i个顺序统计量是该集合中第i小的元素。 最小值是第1个顺序统计量(i=1)最大值是第n个顺序统计量(i=n)
2. 中位数是它所在集合的“中点元素”,n为奇数时为(n+1)/2,n为偶数时有两个。
3. 找最大最小值的算法,一般人可能以为需要2(n-1)次比较,实际上只需要最多3⌊n/2⌋次比较,使用的技巧是: 将一对元素比较,然后把较大者于max比较,较小者与min比较,因此每两个元素需要比较3次。
如何设定当前最小值和最大值取决于n是偶数还是奇数。如果n为奇数,就将最小值和最大值都设置为第一个元素,然后成对的处理余下元素。如果n是偶数,就对前两个元素做一次比较,已决定最大值和最小值的初值。然后如同n是奇数一样,成对处理余下元素。
比较次数, n为奇数:3⌊n/2⌋ n为偶数:3(n - 2)/ 2 + 1 = 3n/2 - 2
#include <stdio.h> #include<stdlib.h> #include<time.h> int count = 0; int compare(int a, int b) { count++; return a-b; } #define MAX(a, b) compare(a,b) > 0 ? (a) : (b) #define MIN(a, b) compare(a,b) > 0 ? (b) : (a) int main() { int i, min, max; int N; srand((int)time(0)); while (1) { N = rand() %10; if (N > 2) break; } printf("N:%d\n", N); int *A = (int*)malloc(N); for (i = 0; i < N; i++) { A[i] = rand() % 15; printf("%d ",A[i]); } printf("\n"); if (N&1) { min = max = A[0]; i = 1; } else { i = 2; if (compare(A[0], A[1]) < 0) { min = A[0]; max = A[1]; } else { min = A[1]; max = A[0]; } } for (; i+1 < N; i += 2) { if (compare(A[i], A[i+1]) < 0) { min = MIN(A[i], min); max = MAX(A[i+1], max); } else { min = MIN(A[i+1], min); max = MAX(A[i], max); } } printf("count:%d, max:%d, min:%d\n", count, max, min); }
4.以期望线性时间选择顺序统计量的方法是以快速排序为模型。如同在快速排序中一样,此算法的思想也是对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。 这就是顺序统计量算法能够如此高效的核心原因所在!
//////////////////////////////////////////////////////////////////////////
/// @file nth_element.cpp
/// @brief 中位数和顺序统计学
/// @details COPYRIGHT NOTICE
/// Copyright (c) 2011
/// All rights reserved.\n
/// 在O(n)的时间内寻找一个数组中的第i个顺序统计量\n
/// 第i个顺序统计量的定义为:该集合中第i小的元素\n
/// 以期望线性时间选择顺序统计量的方法是以快速排序为模型。如同在快速排序中一样,此算法的思想也是
/// 对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select
/// 只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。
///
//////////////////////////////////////////////////////////////////////////
#include <time.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
using namespace std;
namespace ita
{
namespace
{
/// 寻找v数组的子集[begin_index, end_index]中的第i个元素顺序统计量,0 <= i < end_index-begin_index
int _NthElement( vector<int> &v, int const begin_index, int const end_index, int const n )
{
//这个判断纯粹只是一个加速return的技巧,没有这个判断算法也是正确的!
if ( begin_index == end_index )
{
return v[begin_index];
}
//随机取样
int swap_index = rand() % ( end_index - begin_index + 1 ) + begin_index;
swap( v[swap_index], v[end_index] );
//根据最后一个主元进行分割成两部分
int i = begin_index;
for ( int j = begin_index; j < end_index; ++j )
{
if ( v[j] < v[end_index] )
{
swap( v[i++], v[j] );
}
}
swap( v[i], v[end_index] );
//主元是本区间的第k个元素顺序统计量,0<=k<size
int k = i - begin_index;
if ( n == k )
{
//找到了
return v[i];
}
if ( n < k )
{
//在左区间继续找
return _NthElement( v, begin_index, i - 1, n );
}
else
{
//在右区间继续找:由于主元是第k个元素顺序统计量(0<=k<size),所以小于等于主元的元素有k+1个(包括主元),因此寻找右区间的第n-(k+1)个顺序统计量
return _NthElement( v, i + 1, end_index, n - k - 1 );
}
}
}
/// @brief 寻找v数组中的第i个顺序统计量,0<=i<size
///
/// 以快速排序为模型。如同在快速排序中一样,此算法的思想也是
/// 对输入数组进行递归划分。但和快速排序不同的是,快速排序会递归处理划分的两边,而randomized-select
/// 只处理划分的一边。并由此将期望的运行时间由O(nlgn)下降到了O(n)。
/// @param v 要进行查找操作的集合
/// @param i 查找集合中的第i个顺序统计量
/// @return 集合中的第i个顺序统计量
/// @see int _NthElement(vector<int> &v, int const begin_index, int const end_index, int const n)
int NthElement( vector<int> &v, int const i )
{
return _NthElement( v, 0, v.size() - 1, i );
}
/// 中位数和顺序统计学
int testNthElement()
{
vector<int> v;
srand((int)time(0));
for ( int i = 0; i < 10; ++i )
{
v.push_back( ( rand() % 1000 ) );
}
copy( v.begin(), v.end(), ostream_iterator<int>( cout, " " ) );
cout << endl;
for ( int i = 0; i < 10; ++i )
{
cout << i << "th element is:" << NthElement( v, i ) << endl;
}
return 0;
}
}
using namespace ita;
int main()
{
testNthElement();
}
相关文章推荐
- 【算法导论 第9章 中位数和顺序统计学】
- 算法导论学习笔记-第九章-中位数和顺序统计学
- 算法导论 第9章 中位数和顺序统计学(线性时间选择算法)
- 《算法导论》读书笔记之第9章 中位数和顺序统计学 最坏情况是线性时间的选择算法
- 算法导论——第六章——中位数和顺序统计学
- 算法导论 第9章 排序和顺序统计学算法导论
- 算法导论 第9章 中位数和顺序统计学(线性时间选择算法)
- 算法导论第9章中位数和顺序统计学
- 排序和顺序统计学(算法导论)
- 算法导论(第9章-中位数和顺序统计学)最大值和最小值
- 《算法导论》读书笔记之第9章 中位数和顺序统计学
- 算法导论学习笔记-第9章 中位数和顺序统计学
- 算法导论代码 第9章 中位数和顺序统计学
- 中位数和顺序统计(线性时间)算法导论9.2
- 《算法导论》读书笔记之第9章 中位数和顺序统计学
- 顺序统计学(算法导论第九章)
- 算法导论第9章 中位数和顺序统计学
- 《算法导论》读书笔记之第9章 中位数和顺序统计学
- 第九章中位数和顺序统计学之“查找第i小的元素(递归版)平均运行时间为O(n)算法”