移動內存算法

1,002 views
923 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,002
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

移動內存算法

  1. 1. 120 程序员 Technology技术 本期的问题是: 对于有 K个元素的数组int a[K]={....};写一个高效算法将 数组内容循环左移 m 位 比如:int a[6] ={1,2,3,4,5,6},循环左移 3 位得到结果 {456123}, 要求: 1.不允许另外申请数组空间,但可以申请少许变量; 2.不允许采用每次左移。 这是一个有趣的问题,有朋友给出过一个很简单的解法: 1.将整个数组倒排; 2.将前 k-m 个元素和剩下的 m 个元素分别倒排。 这个算法需要对每个数组元素做两次写操作,具体而言是 2n次堆变量写操作和 n次栈变量写操作(因为倒排时调用交换 算法需要一个中间变量),有没有一种方法,只对数组元素进行 一次写操作就完成移动? 最直观的想法,就是从第一个元素开始,把它一步移动到 最终的目的位置,而该位置原有的元素的值取出,移动到它的 新位置。递归进行这个步骤。 首先,我们在数学上很容易理解,这是一个一一对应的映 射,绝不会在一个位置上出现两次移动。所以不会出现移动的 递归过程中途指向了已经移动过的元素。 那么,这个递归过程唯一的终止条件就是,当前移动的元 素,目的位置是移动过程的起始位置。 有兴趣的朋友不妨在纸上推演一下这个过程,并不复杂。 多试验几种组合,细心的朋友也许会发现,这个递归过程有时 候可以遍历整个数组,有时候则会跳过若干元素。这其中有没 有什么规律? 如果按照元素的索引下标标示元素,0到k-1中的任意元素 i,会移动到什么位置? 如果 i小于 m,它会移动到k-m+i,否则,它的新位置是i- m。这个过程其实可以统一在一个算式下: 对于任意的 i,它的新位置 i' = ((k - m) + i )%k。 那么,我们可以定义这个循环链:取整数 i0,使得 0=<i0 <m,定义 i1=((k-m)+i0)%k,i2=((k-m) + i1)%k……ix = ((k-m) + ix-1)%k。当 ix=i0 时循环终止。此时有i0 = ix = ((k-m) + ix-1)%k = ……=(x(k-m)+i0)%k。 因为我们知道 0=<i0<m,所以有 0 = x(k-m)%k,也就 是说,x(k-m) = yk,因为我们是遇到第一个x(k-m) = yk就 终止,显然等号左右的值就是 k-m和 k的最小公倍数。根据数 论知识,我们知道,k,m,k-m 这三个数有最大公约数 q,使 得 k=aq,m=bq,k-m=cq,a,b,c三个数两两互质。进而推出 x = a,y = c。也就是说,这个递归过程会经历 a 步。如果 k 和 m互质,a=k,递归过程会遍历整个区间。那么,它可以完成 对整个区间的移动操作。而如果k和 m的最大公约数q>1呢? 在这里有一个现象,任意两个 i,它们的差要么是 0,要么是 k 和 m 的最大公因是 q的整倍数。有兴趣的朋友可以尝试证明一 下。因为我们知道整个过程一共 a 步,k=aq,那么,i到 ix的序 列会形成一个步长为q的等差序列。所以,我们要移动整个0到 k-1区间,应该对 0 到 q-1 的元素应用这个递归算法。 这样的算法只需要一次堆变量写操作,但是因为需要一个 中间变量储存移动的元素,需要2n次栈变量写操作。再加上计 算目的地址,每次移动时所需的计算量略大于两次倒排,但是 在堆变量操作方面提高了效率。 本次大部分合格的参赛作品都采用了冒泡算法,通过两两交 换邻接元素来移动数组。有朋友还在来信中说明冒泡排序是线性 复杂度。这是错误的。冒泡法是o(n2)的,比起前面两种算法,冒 泡的效率太过低下。很多朋友在m大于或小于k/2时采用的不同 的左 /右位移代码,其实只要理清了算法,不必要做此判断。只 有一位ID为njwind 的朋友想到了利用数论知识。虽然他的代码还 达不到实用标准,但是这应该是本次比赛最好的作品了: #include <stdio.h> #include <stdlib.h> #define Len 6 /* 求最大公约数的函数 */ int CommonFactor(int m,int n){   int tmp;   if(m<n) {       tmp = m; m = n; n = tmp;   }   tmp = m % n; 移动内存算法 □ 文 / 刘鑫 智慧擂台 >>>
  2. 2. Programmer 121   while(tmp != 0) {     m = n;  n = tmp;  tmp = m % n;   }   return n; } void display(int *pa){    int i;    printf("array: ");    for(i=0;i<Len; i++)   printf("%d ",pa[i]);    printf("\n"); } void swap(int *n1, int *n2){     int tmp;     tmp = *n1;  *n1 = *n2;  *n2 = tmp; } /* *p 数据指针,aLen:数据长度,k:左循环位数 */ void LeftRotation(int *p, int aLen, int k){    int i,tmp,factor;    k = k % aLen;    if ( k == 0) return;    k = aLen - k;    factor = CommonFactor(aLen, k);    if (factor == 1){      i = 0;      do{          i = (i+k)%aLen;          swap(p, p + i ) ;      } while(i != 0);    }    else{        for (i = 0;i < factor; i++){          tmp = i;          do{              tmp = (tmp+k)%aLen;              swap(&p[i], p + tmp ) ;          } while(tmp != i);        }    } } int main(int argc, char *argv[]){   int i,k,a[Len]={1,2,3,4,5,6};   for(i=0;i<8;i++){       for( k = 0; k<Len ;k++)  /* 数组赋值 */          a[k] = k+1;       k = i;  display(a);       printf("Len: %d, left rotation: %d\n",Len,k);       LeftRotation(a,Len,k);       display(a);   printf("---------------\n");  }   system("PAUSE");  return 0; } 以下是用 C++实现的一次移动算法代码,并进行了泛化, 使之可以适用于不同的类型,包括用不同整型变量表示k,m等 参数的应用: 求最大公因数,辗转相除,可以应用于任何求最大公因式/ 数的场合,只要参数类型支持求余和赋值: template<typename intT> intT HCF(const intT & x, const intT & y) {     intT a = x, b = y, r = a%b;     while(r != 0) {         a = b; b = r; r = a%b;     }     return b; }; 区间移动,支持任何类型的数组: template<typename T,typename U> void Carry(T point[], const U & len, const U & m){     U start = 0,p = 0;     T pv;     U hloc = HCF(len, m);     U step = len - m;     for (U i = 0; i<hloc; i++) {         p = i; start = p; pv = point[p] ;         do {             p = (step+p)%len;             std::swap(point[p] , pv);         }while(p != start);     } }; 用迭代器实现的区间移动,可以应用于 STL 容器等场合: template<typename iterT,typename U> void Carry(iterT &iter, const U & len, const U & m) {     iterT nb = iter + (len-m);     iterT p;     iterT start = point;     iterT pm = point + m;     iterator_traits<iterT>::value_type pv;     U hloc = HCF(len, m);     for (U i = 0; i<hloc; i++) {         p = point + i;         start = p; pv = *p ;         do {             if (p < pm)                 p = nb + (p - point);             else                 p -= m;             std::swap(*p , pv);         }while(p != start);     } }; 代码中使用了 std::swap 函数来交换变量值,这个函数在 <algorithm>中。另外为了析取迭代器的值,我使用了<iterator> 中的iterator_traits操作。有兴趣的朋友不妨编写一个适用于普通 指针的偏特化版本。 两步倒排算法的实现很简单,在这里就不写出来了。重点 是采用一个足够简单的交换和计算中界的代码。这样的功能,C 风格的代码就可以做到足够好了。 有些朋友认为直接改变索引就可以重排数组,对于某些高 级语言,例如 Python中的内置线性容器list,这是可以的。但是 在C/C++中,数组和链表是不同的概念,数组是内存堆中的连 续块,不可以用这种方法来对待。对于C/C++程序,我们经常 要直接操作内存,要关注算法的复杂度,这并不是缺点,相反, 无论高级的虚拟机语言有多少好处,总需要有底层的技术来管 理内存,用尽可能高效的算法处理面向硬件的问题。否则,用 什么来开发虚拟机呢?这个算法问题实际上在内存管理,例如 虚拟机的实现方面,就有很大意义。在虚拟机内存管理中,我 们面对的总是不够快的 CPU和不够大的内存,需要有一个高效 的算法来移动虚拟机管理的数据。对于Java或 C#程序员,这 个过程通常是透明的,但是这不表示它不存在。而默默支撑起 虚拟机的,正是这些 C/C++ 代码。 >>> 智慧擂台
  3. 3. Programmer 123 特性并把这个构件放到一个特定的地方, 这个构件就不仅仅会收到一个位置协调的 消息,并且会记住为什么它会被抓取。例 如,如果它被抓取同另外一个构件的左边 对齐,这个时候该构件会始终保持它们的 左对齐格式,即使它们的绝对位置发生了 改变。 NetBeans中有双路编辑——使用一个可 视化设计器和一个文本编辑器。一些可视化 设计器已经实现了这种方式。但是NetBeans IDE使用保护模块来防止用户编辑代码。 Eclipse的 Visual Editor Visual Editor是一个开源的 Eclipse 编 辑器。它同 JDT、PDE等其它 Eclipse 的工 具项目一样,是一个全新的Eclipse工具项 目。它可以进行可视化的编辑Java GUI程 序,也能编辑可视化的Java Bean组件。它 能与 Eclipse的Java Editor集成在一起,当 在 Visual Editor中编辑图形界面时,会立 即反馈到 Java Editor中的代码,反之亦 然,即无保护的双路编辑。 Visual Editor支持Swing和AWT的可视 Java组件开发。由于这个Framework设计的 具有通用性,它也可以很容易的实现C++ 或其它语言下可视化开发。其将来的版本 (从 1.0 开始),将会支持SWT的开发。 Visual Editor目前支持所有的传统的 布局管理器。另外,SWT Designer也是流 行的 GUI 编辑器,不过是收费的。 JBuilder 的布局管理器 除了经典的布局管理器外,JBuilder 提供了五个方便的布局管理器,它们是: (1)XYLayout 布局管理器:组件所在 的位置通过相对于左上角坐标确定,其大 小通过宽度和长度确定,当窗口大小发生 变化时,组件的位置和大小保持原位。 (2)PaneLayout 布局管理器:通过百 分比规定组件所占容器空间,当用户界 面调整时,组件的大小也会相应调整。 (3)VerticalFlowLayout 布局管理器: 它和FlowLayout 类似,只不过它以垂直而 非水平的方式排列组件。 (4)BoxLayout2 布局管理器:它是 Swing 标准包中javax.swing.BoxLayout 布 局管理器的一个包裹类,允许你通过查 看器的选择,间接使用BoxLayout的功能。 BoxLayout 将几个组件组合在一起,以水 平或者垂直的方式排列,窗口大小调整 时,组件大小不会随着改变。 (5)OverLayOut2 布局管理器:它是 Swing 标准包中 java.swing.OverLayout 布 局管理的包裹类,允许你通过察看器的 选择,间接使用 OverLayout 的功能。 GUI小结 GUI设计部分,我更喜欢NetBeans,相 信很多用户也跟我一样,因为用起来真 的很方便。 曾看见有人说,在拖拽组件时,却不 能更改代码,觉得很别扭。其实我觉得受 保护的双路编辑才是应该推崇的方法, 这也是对 IDE完美工作的一种保证。 二.编译、运行、调试、打包 本节将比较三个平台在编译、运行、 调试、打包几个必要开发步骤中的具体 表现。 NetBeans 在代码行开头点击即可设置 / 取消 断点;支持条件断点、单步执行等流程控 制功能;支持局部变量、监视、堆栈显示等 功能;支持会话、线程的查看及修改;提 供了完善的远程调试功能;基于Ant,可 通过脚本支持调试。 Eclipse 带有专用的 Debug 视图并能自动切 换;其Debug的功能和Delphi的Debug比较 相似,Inspect、Watch等应有尽有;支持反 汇编、内存、堆栈、寄存器显示等高级功 能;支持会话、线程的查看及修改;似乎 无远程调试;可根据模块的需求扩展。 JBuilder 高度集成,十分丰富的调试环境,支 持以上2款IDE的全部功能;4种编译器及各 自特有的编译选项:Borland Make、Borland Make(JB8)、Project javac、javac;智能单步、 智能源码、智能交换;多线程调试;内置混 淆打包;JBuilder的调试环境可谓傲视群伦, 开发纯java程序时,使用起来十分方便。 三.WEB 与 J2EE 开发 JSP、Servlet NetBeans支持Servlet 2.4和JSP 2.0。 支持使用 Tomcat 5 部署和调试两层 J2EE 1.4 和 1.3 应用程序。以下是 NetBeans 为 Web应用程序开发提供的便利:内置Tomcat 服务器支持;生成和维护部署容易的内 容,包括添加到项目中的 Servlets进行注 册;生成与维护具有编译、清除、测试、 打包、部署指令的Ant脚本。该脚本使开 发者无需手动将文件移动到服务器;用 于编辑 Servlets、JSP、HTML和标签库的 代码自动完成和帮助;提供“编译JSP”命 令,使用这一命令可以帮助开发者在部 署前检测JSP文件的错误,不管这些错误 出现在编译过程中还是 J S P 文件向 Servlets 转换过程中;提供全面的调试支 持,包括“步进JSP文件”以及“跟踪HTTP 请求”;大量的 Web模板:JSP、Servlet、 Filter、Web应用侦听器、标签库描述、标 签、标签处理、Web Service、消息处理、 Web客户端等;描述文件web.xml可视化 编辑器;监测 HTTP 事务处理。 E c l i p s e 在不安装 L O M B O Z 或者 MyEclipse 的情况下编写 Web应用程序真 是麻烦之极。首先你需要手动安装Tomcat 或者其他服务器,然后在Eclipse 中配置; 接下来编写代码,再更改web.xml文件做 部署。最后很可能因为配置不好的原因, 无法启动或者部署Web服务器。好在对web .xml编辑时也是可视化的。 JBuilder一体化的解决办法倒是很好, 并且通过 OpenTools 框架支持很多种Web 服务器。当然也需要手动安装这些服务 器,并且添加这些服务器的 Lib到你的项 >>> SUN 征文
  4. 4. 124 程序员 Product & Application产品&应用 框架名称 Struts Hibernate Spring Cocoon JSF NetBeans 5.5 Eclipse 3.x JBuilder 2005 支持,向导,文档丰富 MyEclipse 良好支持 支持,向导,文档丰富 Nbxdoclet支持,5.5加 Hibernate Syn 支持 手动配置 入数据库结构和实体类 的相互生成和向导 支持,向导,文档丰富 Spring plugin 4 Eclipse 手动配置 Spring for JBuilder2005 OpenTools 暂没找到直接支持 Lepido 支持 支持,向导,文档丰富 支持,向导,文档丰富 JSF Tool Project 支持 支持,向导,文档丰富 目中。JBuilder还为各种部署文件提供了 方便的编辑工具: l web.xml:Web Module DD Editor l struts-config.xml:Struts Config Editor l faces-config.xml:Faces Config Editor 另外JBuilder也对Jsp和Servlet提供监 听器,监视各种事件的发生。 JBuilder 提供了最为完善的Web开发 环境,其次 Netbeans功能也十分丰富。 EJB/J2EE NetBeans 从 NetBeans IDE 4.1 版本开始, NetBeans 提供创建和编辑 EJB丰富的向导 和编辑器。 NetBeans 5.0提供以下针对企业开发 的支持:建立和编辑EJB的可视化编辑器; 控制 CMP和实体 Bean关系的可视化编辑 器;通过鼠标点击就可以在Web模块中加 入对 EJB的调用;完整的组装、部署、运 行和调试企业应用程序的支持;注册和 测试Web Service;通过鼠标点击就可以在 Web模块或EJB中加入对Web Service的调 用;由EJB或者Java类自底向上的建立Web Service;由WSDL文件自顶向下的建立Web Service;可以导入其他 IDE的J2EE项目,前 提是其与 BluePrints标准兼容。 Eclipse与 Lomboz Lomboz是Eclipse的一个主要的开源插 件(open-source plug-in),Lomboz插件能 够使Java开发者更好的使用Eclipse去创建, 调试和部署基于J2EE的 Java应用服务器。 Lomboz 的主要功能有:使用 HTML, Servlets,JSP 等方式建立 Web应用程序; J S P 的编辑带有高亮显示和编码助手、 JSP语法检查;利用Wizard创建Web应用、 EJB应用和 EJB客户端测试程序;支持部 署 EAR、WAR和 JAR;利用xDoclet 开发符 合 EJB 1.1、2.0和 3.0的应用;能够实现 端口对端口的本地和远程的测试应用服 务;能够支持所有的有可扩展定义的Java 应用服务;能够利用强大的Java调试器调 试正在运行的服务器端代码(JSP&EJB); 通过使用 Wizard 和代码生成器提高开发 效率;创建Web服务客户端的WSDL形式 的文件。 Lomboz 适用的服务器有:Apache Tomcat,JBOSS,JOnAS,Resin,Orion,JRun, Oracle IAS,BEA WebLogic Server和 IBM WebSphere等。 三种平台对框架的支持比较 小 结 对于J2EE 开发,Eclipse的各种支持是 对全面和及时的,但是否成熟,则未必。并 且开发者经常被成堆的插件弄得头昏脑胀。 NetBeans的最新版本提供了对最新标 准,如EJB 3.0 的支持,并且内置对常见 框架支持。这说明,现在完全能够全面转 向 NetBeans。 四.J2ME 开发 NetBeans Mobility Netbeans IDE和 Mobility Pack 提供的 项目管理功能非常出色,将目标平台、应 用程序描述符、编译运行、混淆、签名等 功能集成在了一起。开发者只需要选中 项目,右键选择属性即可配置上述选项。 值得注意的一点是,当项目中使用了图 片或者媒体文件等资源的时候,应该在 “库和资源”选项中讲资源文件所在的文 件夹添加到“捆绑的库和资源”中。避免 在 Java 程序中访问资源的时候抛出空指 针异常。Mobility Pack 还直接集成了 Proguard 混淆器,可以设置混淆的级别, 混淆的级别越高,混淆的力度就越大。 Mobility Pack提供了可视化用户界面 设计器,开发者可以用鼠标拖拽的方式 设计应用程序的用户界面,通过流程控 制器实现界面之间的跳转,而不用编写 任何代码。无线连接向导是Mobility Pack 另一新特性,能方便快速的开发出端到 端的企业级应用程序,服务器端只提供 需要导出的服务类,Netbeans IDE会自动 生成服务器端的 Servlet 以及客户端用于 连接网络的代码。虽然上述两个功能使 用起来非常方便,但是缺乏灵活性,你很 难再更改开发工具为你自动生成的代码。 Eclipse 和 EclipseME 不仅要下载安装 EclipseME插件,还 要安装 WTK,并且在 Eclipse环境中配置。 使用 Eclipse 搭建 J 2 M E 的开发环境比 Netbeans IDE稍显复杂。事实上,管理 Eclipse 的各种插件已经让开发者头疼不 已,有些插件的更新还很难保证。 JBuilder 和 WTK 从JBuilder 9版本开始,Borland将WTK 直接集成到了开发工具内。如果使用以 前版本的 JBuilder,那么需要首先安装 MoblieSet 插件。 小 结 其实,各种开发工具只是以自己的 方式对 M I D P 应用程序的开发进行了封 装, MIDP应用程序的开发流程都是一样。 事实已经证明,除了使用手机供应商自 己的套件外(如 Nokia Toolkit),WTK和 NetBeans Mobility的用户最多——这方面 SUN 征文 >>>

×