Virtual Initialization - 虚初始化

本文探讨了如何设计一种数据结构,它在保持数组随机存取性能的同时,能以O(1)的时间复杂度进行初始化。通过引入额外的标记数组和巧妙地利用数据,实现了在不实际赋值的情况下,读取未修改元素时返回默认值,从而解决了大数组初始化效率低的问题。

这是我上Dean教授算法设计课的作业,题目描述如下:

One problem with arrays is that they must typically be initialized prior to use. On most computing environments, when we allocate an array of O(n) words of memory they start out filled with “garbage” values (whatever data last occupied that block of memory), and we must spend O(n) time setting the words in the block to some initial value. In this problem,
we wish to design a data structure that behaves like an array (i.e., allowing us to retrieve the ith value and modify the ith value both in O(1) time), but which allows for initialization to a specified value v in O(1) time as well. That is, if we ask for the value of an element we have not modified since the last initialization, the result should be v. The data structure should occupy O(n) space in memory (note that this could be twice or three times as large as the actual space we need to store the elements of the array), and the data structure should function properly regardless of whatever garbage is initially present in this memory. As a hint, try to combine the best features of an array and a linked list.

题目大意就是说我们在使用数组之前需要对数组进行初始化,但是初始化数组的时间复杂度是O(n),如果数组非常大的话是非常耗时的,因此我们希望能够设计一种数据结构,能够像数组一样可以随机存取,但是可以在O(1)的时间内初始化数组。


思路:首先可以确定的肯定不是真正的初始化数组的每个元素,必然是借助某些标志可以判断某一位是不是被赋值了,如果是的话就返回其值,否则就返回一个默认值。关键在于如何利用辅助空间标记初始化的信息。

如果我们开辟一个同样大小的数组用于标记原数组的哪一位是不是被赋值了,这样并不可行,因为新开的数组中充满了垃圾数据,也就是原来这块内存中有什么值在重新被赋值之前还是这个值。所以我们要消除随机数据的影响。可以再开辟一个同样大小的数组,帮助我们完成这个功能。

我们可以声明这个数据结构如下:

int data[N];
unsigned int index[N];
unsigned int pos[N];
int number = 0;
其中index数组用于记录某位置是第ith被赋值的,pos用于记录第ith个被赋值的位置。number用于记录有多少个数被赋值了。

在为某一位赋值的时候,我们需要做如下操作:

data[i] = val;
index[i] = number;
pos[index[i]] = i;
number++;

在我们取某一位的值的时候,我们需要判断:

1.如果index[i] < number && pos[index[i]] == i,那么说明这一位已经被赋值过了,因此可以返回其值

2.否则,就返回一个默认值VAL

这样做之所以可以是因为

1.如果在最开始的时候我们访问数组的某一位,则index[i] < number是永远为false的,因为任何无符号数都不会比0小。

2.当数组中已经有一些位置被赋值,在我们访问某未被赋值的位置时,我们直到index[i]此时还未被赋值,也就是其值为垃圾数据。即使碰巧index[i] < number,那么pos[index[i]]也不等于i,因为pos[index[i]]在此前已经被赋值了。


举个栗子,数组和标记位的初始如下:

data    x  x  x  x  x  x  x  x  x  x  x  x
index   x  x  x  x  x  x  x  x  x  x  x  x
pos     x  x  x  x  x  x  x  x  x  x  x  x
number = 0;
现在我们想访问data[6]的值,首先查看index[6],此时index[6]中的数据是随机的,但是index[6]必然是>= 0的,因为数组是无符号整型,因此返回默认值VAL.

现在为其赋值data[4] = 9,数组变化如下:

data    x  x  x  x  9  x  x  x  x  x  x  x
index   x  x  x  x  0  x  x  x  x  x  x  x
pos     4  x  x  x  x  x  x  x  x  x  x  x
number = 1;
然后访问data[4],发现满足 index[4] = number && pos[index[4]] == 4,因此返回data[4]。

再访问data[6],如果data[6]中的随机数据恰好小于number,即为0,那么我们再查看pos[0]的,我们直到pos[0]的值在上次被赋值为4,因此pos[0] != 6,所以返回默认值VAL.

这样就完成了满足O(1)时间初始化的任务和证明。






dnSpy是目前业界广泛使用的一款.NET程序的反编译工具,支持32位和64位系统环境。它允许用户查看和编辑.NET汇编和反编译代码,以及调试.NET程序。该工具通常用于程序开发者在维护和调试过程中分析程序代码,尤其在源代码丢失或者无法获取的情况下,dnSpy能提供很大的帮助。 V6.1.8版本的dnSpy是在此系列软件更新迭代中的一个具体版本号,代表着该软件所具备的功能与性能已经达到了一个相对稳定的水平,对于处理.NET程序具有较高的可用性和稳定性。两个版本,即32位的dnSpy-net-win32和64位的dnSpy-net-win64,确保了不同操作系统架构的用户都能使用dnSpy进行软件分析。 32位的系统架构相较于64位,由于其地址空间的限制,只能支持最多4GB的内存空间使用,这在处理大型项目时可能会出现不足。而64位的系统能够支持更大的内存空间,使得在处理大型项目时更为方便。随着计算机硬件的发展,64位系统已经成为了主流,因此64位的dnSpy也更加受开发者欢迎。 压缩包文件名“dnSpy-net-win64.7z”和“dnSpy-net-win32.7z”中的“.7z”表示该压缩包采用了7-Zip压缩格式,它是一种开源的文件压缩软件,以其高压缩比著称。在实际使用dnSpy时,用户需要下载对应架构的压缩包进行解压安装,以确保软件能够正确运行在用户的操作系统上。 dnSpy工具V6.1.8版本的发布,对于.NET程序员而言,无论是32位系统还是64位系统用户,都是一个提升工作效率的好工具。用户可以根据自己计算机的操作系统架构,选择合适的版本进行下载使用。而对于希望进行深度分析.NET程序的开发者来说,这个工具更是不可或缺的利器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值