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

2024-05-09 15:38

本文主要是介绍编程珠玑之第二章:杂耍算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文系转载链接 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);                    //找到重复执行的次数stepfor(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次移动。


这篇关于编程珠玑之第二章:杂耍算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/973795

相关文章

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Springboot实现推荐系统的协同过滤算法

《Springboot实现推荐系统的协同过滤算法》协同过滤算法是一种在推荐系统中广泛使用的算法,用于预测用户对物品(如商品、电影、音乐等)的偏好,从而实现个性化推荐,下面给大家介绍Springboot... 目录前言基本原理 算法分类 计算方法应用场景 代码实现 前言协同过滤算法(Collaborativ

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.