内存移动算法

内存移动算法

个人笔记,转载请联系本人

本篇笔记也被放在我的github上,那里有详细的代码。

概述

算法课上老师介绍了内存移动算法。说实话,当时并没有听懂。原因是对这个没什么感触,体会不到关键点在哪里。

问题背景

如何高效的把一块内存上的数据循环移动k位?要求时间复杂度和空间复杂度尽可能小。

由于课上已经讲过右移的情形,这里只讨论左移。

在平常的编程中,循环移动不是个很难的问题。可以每次把所有的元素移动一位,总体移动k次就可以完成任务。但显然可以通过计算来避免多次移动这个重复操作。

问题分析

现在来做简要的分析。设待移动的序列为:

a0,a1,a2,.....,ai,.....an1

对于元素 ai ,向左移动k位后,位置在 ik 。记做:

NewIndex(i,n,k)=ik

考虑到越界情况,应该修正为:

NewIndex(i,n,k)=(ik)modn

其中,设定取模运算的结果为正数。
这个结论是显而意见的,因为这个问题的移动本来就是循环移动。从而可以得到初步的移动思路:

move_right(col,n,k):
  new_col=col.clone()
  for i in [0,col.size()):
    new_pos=NewIndex(i,n,k)
    new_col[new_pos]=col[i]
  return new_col

这个思路看起来挺合理的。但想想,一个内存移动的算法能允许O(n)的额外空间消耗吗?只怕是还需要改进一下才行。

在上面的算法中,没有利用好已有的空间,也就是col的空间。每一个col中的存储空间,在提供了元素值后,实际上是没有其他作用的。在这个元素的移动完成后,并没有其他作用,完全可以复用它的空间。如果限定只能使用常数个存储空间,就只有充分利用这个想法,钻钻空子。

考虑这个实例:

0,1,2,3,4,5,6,7

如果要求左移5位,可以得出这样的移动序列

0 -> 3 -> 6 -> 1 -> 4 -> 7 -> 2 -> 5 -> 0

可以看到,整个序列形成了循环。那么,是不是任意情况下,都可以形成一个循环呢?

对于元素 ai ,移动后位置是:

i -> (i-k) mod n

而元素 a(ik)modn 移动后位置是:

(i-k) mod n -> ((i-k) mod n - k) mod n

为了方便理解,不妨再假设k是小于n的。即 k=kmodn 实际上,对于下面的推到,也并不失k大于n时的一般性。

现在,根据摸运算的规律(可以证明):

(amodnbmodn)modn=(ab)modn

那么,

((ik)modnk)modn=((ik)modnkmodn)modn=(i2k)modn

即,

  i -> (i-k) mod n -> (i-2k) mod n

这就得到了:

i -> (i-k) mod n ->....-> (i-p*k) mod n

在p等于n时,明显开头结尾两项是重合的。也就形成了一个循环。
但这不表示,循环序列一定包含了所有的元素。也就是说,移动过程不一定由一个循环确定,还可能需要多个循环。在附上的代码 move-oder.cpp 中,就有这种情况。

对于几个循环可以确定一个移动序列,我确实没有办法。老师的解法确实不错,我想不出来。

设y=q*k/n,k=a*e,n=b*e,e是k和n的最大公约数,那么

kn=yq=ab

因为a b 互质,为确保循环序列的长度最小,q一定是最小的,那么
q=b

也就是说,q个循环可以照顾到每个元素。其中,q是n除以k和n的最大公约数的所得到。

算法描述

现在已经可以给出改进后的算法的位代码了。

move_left(col,n,k):
    e=最大公约数(k,n)
    for j in [0,e):
        backup=col[i]
        new_pos=NextIndex(j,n,k)
        for i in [0,n/e):
            temp=backup
            backup=col[new_pos]
            col[new_pos]=temp
            new_pos=NextIndex(new_pos,n,k)
    return col

额外消耗的空间复杂度为O(1)
时间复杂度 T(n)=n/eq=1(qi=04) (不求了,但是根据伪代码,没有做多余的操作,每个内循环都是对应一个元素的移动哦,总体时间复杂度应该是O(n))

代码实现

见memory-copy.cpp

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值