编程珠玑之第二章:杂耍算法

杂耍算法 杂耍算法步骤: 1)先将x[0]移到临时变量t中 2)将x[i]移动到x[0]中,x[2i]移动到x[i]中,依次类推 3)将x中的所有下标都对n取模,直到我们又回到从x[0]中提取元素。不过这时我们从t中提取元素,结束。 问题描述: 请将一个具有n个元素的一维向量x向左旋转i个位置。例如,假设n=8, i=3,那么向量abcdefgh旋转之后得到向量defghabc。 ... 阅读详情

本文系转载链接 http://www.cnblogs.com/solidblog/archive/2012/07/15/2592009.html

作者在文中的证明思路清晰,不过我只看懂了辗转相除法的证明,杂耍的证明未看懂,但是仔细模拟杂耍算法的过程会有比较直接的体会。


问题:将大小为n的数组向左移动i位

基本原理:

     1)先将x[0]移到临时变量t中
    2)将x[i]移动到x[0]中,x[2i]移动到x[i]中,依次类推
    3)将x中的所有下标都对n取模,直到我们又回到从x[0]中提取元素。不过这时我们从t中提取元素,结束。

     移动过程中不会出现数据丢失的问题,即x[i]已经放到正确的位置后,x[i]就是空位,那么后面的x[2i]便可以赋值给x[i]。问题的关键是理解应该执行几次上面的操作,证明的结果是重复执行i和n的最大公约数gcd(i, n)次,在纸上随便写两个正整数,按照上面的过程走一遍,就会发现,这样做事正确的。接下来是链接作者的描述,其中辗转相除的代码有Bug,已经修改,修改策略可以参考《挑战程序设计竞赛》中的2.6.1节。


#include<stdio.h>

//使用辗转相除法求最大公约数
int gcd(int a, int b)           //b会不断变小直到0
{
    if (b == 0)
        return a;
    return gcd(b, a%b);         //a与b的最大公约数 == 较大数a与较小数b的余数 (如果一开始a小于b,那么此句会进行交换)
}

//杂耍算法
void rotate(char* a,int n,int i)
{
    int step = gcd(n,i);                    //找到重复执行的次数step
    for(int j = 0; j < step; j++)           //分别以0到step-1作为起点
    {
        int temp = a[j];
        int current = j;
        while(1)
        {
            int next= (current + i) % n;
            if(next== j)                   //如果要提取(向左移动i位)起点值,则退出
            {
                break;
            }
            a[current] = a[next];          //向左移动i位
            current= next;
        }
        a[current] = temp;                  //将起点的值temp赋给最后空出来的位置(完成向左移动i位的操作)
    }
}

int main()
{
    char a[9] = "abcdefgh";
    rotate(a,8,3);

    printf("%s\n",a);
    return 0;
}

以下按原文格式转载:

4.辗转相除法求最大公约数

两个整数的最大公约数是能够同时整除它们的最大的正整数。辗转相除法基于如下原理:

 

两个整数的最大公约数等于其中较小的数和两数的差的最大公约数。

 

我们来看一下这个原理的证明:
设两数为a、b(b<a),用gcd(a,b)表示a,b的最大公约数。
r=a mod b,r为a除以b以后的余数,辗转相除法即是要证明gcd(a,b)=gcd(b,r)。

证明步骤如下:
1)令c=gcd(a,b),则设a=mc,b=nc
2)r = a mod b,所以r = a - k*b = mc - k*nc = (m - kn) * c。即,r = (m - kn) * c
3)由r = (m - kn) * c 可知:c也是r的因数
4)可以肯定m - kn与n互质(why?)
    假设他们不互质,必然存在大于1的整数d,使得m-kn = xd, n = yd。那么,m = xd + kyd = (x + ky)d
    那么,a = mc = (x + ky)dc , b = nc = ydc 。=> a,b的最大公约数为dc,而不是c。
5)既然m - kn与n互质,所以c = gcd(r,b)。
结论,gcd(a,b)=gcd(b,r)

(辗转相除法更详细的描述请参照百度百科:http://baike.baidu.com/view/255668.htm。

5.杂耍算法

杂耍算法中如下两点我无法理解:

1.为什么程序要循环执行gcd(i,n)次
2.这个算法是通过什么样的机制就让所有位置上的元素都移动到了预期的位置

程序的走向我是明白的,但是核心算法始终不得其解。
最终还是需要借助网络,搜到了园子内一位朋友写的文章(关于编程珠玑中习题2.3的一点思考
看完以后,有种豁然开朗的感觉,在此多谢这个朋友了。

不过,那篇文章中有些概念的细节讲的不是太清楚,我也是借助网络才有了更清晰的了解。
再次,我来总结个精简吧的吧,写的不好还请各位包涵!

先从几个概念开始:

  1. 同余
    如果两个整数(a,b)除以同一个整数m,如果得到相同的余数k。则称a,b对于模m同余。
     记作a ≡ b (mod m)
  2. 同余类
    所谓同余类是指以某一特定的整数(如m)为模,按照同余的方式对全体整数进行的分类。
    对给定的模m,有且恰有m个不同的模m的同余类。它们是:
       0 mod m,1 mod m,…,(m-1)mod m。
  3. 完全剩余类
    通过2)我们知道,所有的整数以m为模可以划分为m个没有交集的集合。
    从每个集合中取一个整数组成一个集合,则这个集合中的m个整数就不存在同余的整数,这个集合就叫做完全剩余类。

了解了以上三个概念后,我们能够得出如下的结论:

 

如果i和n互质,那么序列:
    0   i mod n     2i mod n    3i mod n    …… (n-1)*i mod n
就包括了集合{0,1,2,……n-1}的所有元素

 

我们为什么会有这样的结论呢,下面来证明一下:
前提条件
    对于模n来说,序列0,1,2,……,n-1本身就是一个完全剩余类(即他们之间两两互不同余)

证明步骤
1)从此序列中任取两个数字xi,xj(0 <= i,j <= n-1),则有Xi≠Xj (mod n),   注:这里由于不能打出不同余字符因此用不等于替代
2)由于i和n是互质的,所以 xi * i ≠ xj * i(mod n)
    =》这就说明,xi从0开始一直取值到n-1,得到的序列0 * i,1 * i,2 *i,……(n-1)*n就是一个完全剩余类。即集合0,1,2,……n-1}

有了以上的结论,如果i和n互质,下面的赋值过程便能完成所有位置的值的移动:
    t = X[0]
    X[0] = X[i mod n]
    X[i mod n] = X[2i mod n]
    …….
    X[(n-2)*i mod n] = X[(n-1)*i mod n]
    X[ (n-1)*i mod n] = t
以上的赋值操作,赋值操作符的两边都得到了一个完全剩余类,也就是说所有的0 ~ n-1的所有位置都被移动过了
请注意第二个操作,X[0] = X[i mod n]。
        该操作决定了整体的导向,该操作将i mod n位置的值移动到了最开始的位置。
        由于i,2i,……之间的偏移量是相同的,所以整个操作实际上就是讲序列向左移动i个位置(超过了开始位置的接到最右边去)


那么如果i和n不互质呢?
自然的想法是利用我们已经得到的结论,让i和n互质,即让i’ = i/(gcd(i,n)),n’ = n/(gcd(i,n))。
这样便构造了一对互质的数, i’和n’。这意味着把整个数组的每g=gcd(i,n)个元素组成块。
由于我们的单位是块元素,每次相当于把g中的一个元素移到最终位置上,所以总共需要g次移动。


【读薄《编程珠玑》】贰 啊哈!算法 这篇文章是[《读薄<编程珠玑>》系列博客][1]的第 贰 篇,在这篇文章中,作者提出了三个问题,并且给出了相应的解决方案,本文阐述了这三个问题以及解决方案,并且对课后习题进行了分析。 问题集合 0x00: 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数(在文件中至少缺失一个这样的数)。在具有足够内存的情况下,如何解决该问题?如果有几个外部的『临时』文件可用,但 阅读详情

相关推荐

简单代码解决排列组合(摸球)问题

从三个红球、五个白球、六个黑球中任意取出八个球,且须有白球,编程输出所有可能的方案。 #include <stdio.h> int main() { int a, b, c; for (b = 1; b<= 5; b++) for (a = 0; a <= 3; a++) for (c = 1; c <= 6; c++) if (a + b + c ==8) printf("红球的个数为=%d,白球的个数为=%d,黑球的个数为=%d\n",

zyt520919的博客 1740

关于编程珠玑中习题2.3的一点思考

出处:http://www.cnblogs.com/HappyAngel/archive/2011/01/16/1936905.html     这两天看到编程珠玑第二章,关于习题2.3中说到杂耍算法执行gcd(i,n)次后即可停止,这里我想了很久为什么?书中提到的Swap Sections解决了我的疑惑,在明白为什么的时候真的 “啊哈”了一下,原来这样,感觉证明非常巧妙,不敢独享,所以复述

思考,思考,再思考~ 1103

编程珠玑》-- 开篇:BitMap算法用于磁盘文件排序的原理与实现

一、问题提出与描述:我们经常会遇到一类问题,这类问题是:有一个大的文件,该文件中存有大量的未经排序的数字,给定限制大小的内存,设计算法实现对该文件中的所有数字的排序,并将排序结果放到新文件中(经常会以时间日期等命名)。比如:有一个文件,该文件中有0~4294967295区间内的2147483648个数字,并且是乱序的,给定内存为1GB,对该文件中的数字进行排序。一般情况下,这类问题有个特点,就是给定

Apollon_krj的博客 2178

编程珠玑-第二章旋转算法

编程珠玑第二章比较精髓,开篇三个题目 1:给定一个包含32位整数的顺序文件,它至多包含40亿个这样的整数,并且整数的次序是随机的,请查找一下此文件中不存在的32位整数(至少必有一个遗漏,为什么?)。在有足够的主存的情况下,你会如何解决这个问题?如果可以使用若干外部临时文件但主存却只有上百个字节,你会如何解决这个问题? 2:请将一个具有n个元素的一维向量向左旋转i个位置。例如,假设n=8,i=3

linluyisb的专栏 1212

编程珠玑第二章 啊哈,算法三个问题

一. 三个问题 A:给出一个顺序文件,它最多包含40亿个随机排列的32位整数。        问题:找出一个不在文件中的32位整数。        注意:题目中没有说,这40亿个数是否是含有重复的数据        条件限制:        1、如果有足够的内存,如何处理?        2、如果内存仅有上百字节(内存不足)且 可以用若干外部临时文件,如何处理? B:类似字符串循环移

better me的博客 1242

编程珠玑第二章 “杂技算法” 和 “翻转算法” Java语言版

编程珠玑第二章 “杂技算法” 和 “翻转算法”  Java语言版 之前一篇文章讲了http://blog.csdn.net/tcpipstack/article/details/8125759字符串翻转里面的 “杂技算法” 和 “翻转算法” 的C语言实现,最近完成了上述算法的Java实现,具体代码如下所示: 杂技算法源码: package chapter2; impor

TCP/IP协议栈学习ing 1194

编程珠玑》习题练习In Python——第二章 啊哈!算法

programming pearls!

vincent_hbl的博客 1024

编程珠玑-第二章(字符串旋转与排序)

关于字符串旋转的一些想法 注意:区别字符旋转和字符串倒序排列的区别,旋转为abcd-》cdad 1.杂耍算法 特点:可以在空间有限的情况下解决问题,但是缓存性能差。#include <iostream> #include <assert.h> using namespace std;/*获取m和n的最大公约数*/ int GCD(int m,int n) { int tmp;

Megustas_JJC的博客 879

编程珠玑】【第二章】问题B

问题B:将一个n元一维向量向左旋转i个位置。例如,当n = 8且i = 3时,向量abcdefgh旋转为defghabc。 方法一、使用一个字节的额外空间开销。 采用每次向左移一位的方法,循环i次。当然也可以使用向右移动的方法,循环length - i次。以向左移动为例,共需要移动i趟,首先把str[0]赋值给临时变量temp,剩余的字符向左移动一位,即str[k]=str[k+1],移动完成...

weixin_30542079的博客 267

向量旋转算法集锦

http://blog.csdn.net/goal00001111/archive/2008/12/29/3635929.aspx   /*   Name: 向量旋转算法集锦   Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处   Author: goal00001111   Date: 28-12-08 23:28   Description:   向量旋转算法:将具有n个元素的向量a向左旋转r个位置。  

llingy的专栏 855

编程珠玑第二章习题2.6(1-4)

1.考虑查找给定输入单词的所有变位词问题。我们只需在问题C进行更改存储方式即可 程序如下,用一个 HashMap<String, ArrayList<String>>来保存结果即可,键为标识,值为同位词构成的listimport java.util.ArrayList; import java.util.Arrays; import java.util.HashMap;class WordInfo

YellowStar5 806

编程珠玑第二章

1. 问题1 问题描述: 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中的32位整数(在文件中至少缺失一个这样的数,为什么?). (1)在具有足够内存的情况下, 如何解决该问题? (2)如果有几个外部的"临时"文件可用,但是仅有几百字节的内存, 又该如何解决该问题? 问题思路: 由于2^32 = 4 294 967 296,超过给出的40亿,因此必定存在32

小猪真的真的真的要减肥了 470

编程珠玑读书笔记——串的转置算法

将一个具有n个元素的一维向量向左旋转i个位置.假若n = 8, i = 3, 那么向量abcdefgh 旋转后为defghabc. (文中代码部分,均为我用c语言实习.编译器为vc6.若出现错误或者有更好的方法请,请留言交流.谢谢~) 1、将待旋转的向量看作是ab两段,假设a比b短,将b分割为b1和b2使b2长度与a相等. 交换a、b2,这样ab1b2变为b2b1a这样a已经在自己最终的位...

weixin_30426065的博客 122
上一篇: longest valid parentheness 最长有效括号问题
下一篇: 大数阶乘问题
沈三水
博客等级 码龄13年 8粉丝 45原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值