[Cyclic Sort] 找到所有数组中消失的数字

leetcode 448:Find All Numbers Disappeared in an Array,难度easy

0. 题干

给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。

找到所有在 [1, n] 范围之间没有出现在数组中的数字。

您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。

示例:

输入:
[4,3,2,7,8,2,3,1]

输出:
[5,6]

1. 代码

将数组元素对应为索引的位置加n
遍历加n后的数组,若数组元素值小于等于n,则说明数组下标值不存在,即消失的数字。

补充说明 C++中的push_back函数:
可以把vector简单理解为动态一维数组,push_back作用是在这个一维数组尾部插入一个元素 其值为
push_back的参数值假设当前vector元素序列为 1 5 7 大小为3,那么 push_back(4)后 元素序列为1 5 7 4大小为4

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        vector<int> res;
        if(nums.empty()) return nums;
        for(int i=0;i<nums.size();i++)
        {
            int index=(nums[i]-1)%nums.size();
// 减1一方面能够,符合数组下标从0开始
// 而且能确保,享受后面+nums.size()和不加之前有明确的区分
            nums[index]+=nums.size();
        }
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]<=nums.size())
//享受不了之前+nums.size()的下标,还是原数组的值,其必然<=nums.size()
// 根据之前下标的处理,不存在的值输出i+1即可
//注意这个边界条件等于号是一定需要的,因为未处理的可能恰好等于nums.size()
                res.push_back(i+1);
        }
        return res;
    }
};

在这里插入图片描述

<think>我们需要同时访问数组的所有元素,这通常在需要并行处理数组每个元素时出现。在硬件描述语言(如VHDL/Verilog)或高级综合(HLS)中,我们可以通过并行化或使用多个处理单元来实现。在软件编程中,我们可以使用多线程、向量化指令(如SIMD)或GPU并行计算来同时访问和处理数组数据。 但是,根据你之前提供的代码片段(HLS编译指令),我推测你是在FPGA上使用高级综合(HLS)进行开发。在FPGA中,我们可以通过展开循环和数组分割来实现同时访问数组的所有元素。 然而,需要注意的是,同时访问数组的所有元素(尤其是大型数组)可能会消耗大量的硬件资源(如寄存器或BRAM端口)。因此,我们需要根据数组的大小和FPGA的资源情况来权衡。 以下是一个使用Vivado HLS同时访问数组所有元素的示例。我们将使用`#pragma HLS UNROLL`来展开循环,这样循环的每次迭代都会并行执行。同时,为了确保数组的所有元素可以被同时访问,我们还需要对数组进行适当的分区(Partition),使得每个元素都存储在独立的寄存器中(或者至少保证每个元素都可以被独立访问)。 假设我们有一个数组,我们希望对数组的所有元素同时执行某个操作(例如,每个元素都加上一个常数)。 代码示例: ```cpp #include "ap_int.h" #define N 8 // 数组大小 void parallel_access(ap_int<8> in[N], ap_int<8> out[N], ap_int<8> increment) { #pragma HLS ARRAY_PARTITION variable=in complete dim=1 #pragma HLS ARRAY_PARTITION variable=out complete dim=1 for(int i = 0; i < N; i++) { #pragma HLS UNROLL out[i] = in[i] + increment; } } ``` 解释: 1. `#define N 8`:定义数组大小为8。注意,在实际应用中,如果数组很大,完全展开和完全分区可能会消耗大量资源,因此通常只对小数组这样做。 2. `#pragma HLS ARRAY_PARTITION variable=in complete dim=1`:将输入数组`in`完全分区,即每个元素都独立存储(相当于拆分成N个独立的寄存器)。 3. 同样,对输出数组`out`也进行完全分区。 4. `#pragma HLS UNROLL`:这个指令告诉编译器将循环完全展开,即生成N个独立的加法器,每个加法器处理数组的一个元素。由于循环被展开,并且数组已经被完全分区,因此每个加法操作都可以在同一个时钟周期内并行执行。 这样,在同一个时钟周期内,数组`in`的所有8个元素都会被同时读取,同时加上`increment`,然后同时写入到数组`out`的相应位置。 但是,如果数组很大(比如N=1024),完全分区和展开会消耗大量的逻辑资源和寄存器,这通常是不现实的。因此,在实际应用中,我们可能需要折中,例如将数组分块(block)或者使用部分展开(partial unroll)和部分分区(cyclic或block分区)。 例如,如果N=1024,我们可以每次处理16个元素(部分展开): ```cpp #define N 1024 void parallel_access(ap_int<8> in[N], ap_int<8> out[N], ap_int<8> increment) { #pragma HLS ARRAY_PARTITION variable=in cyclic factor=16 dim=1 #pragma HLS ARRAY_PARTITION variable=out cyclic factor=16 dim=1 for(int i = 0; i < N; i++) { #pragma HLS PIPELINE #pragma HLS UNROLL factor=16 out[i] = in[i] + increment; } } ``` 这里,`cyclic factor=16`将数组分成16个独立的分区,然后`UNROLL factor=16`将循环部分展开16次,这样每个时钟周期可以同时处理16个元素。整个数组需要1024/16=64个时钟周期完成。 注意:同时访问所有数据(完全并行)只适用于小数组。对于大数组,我们通常采用部分并行(即每个时钟周期处理多个数据)的方式。 如果你不是在HLS环境下,而是在CPU上编程,那么可以使用多线程(例如OpenMP)或SIMD指令(如AVX)来实现并行处理。例如,使用OpenMP: ```cpp #include <omp.h> void parallel_access(int* in, int* out, int increment, int n) { #pragma omp parallel for for (int i = 0; i < n; i++) { out[i] = in[i] + increment; } } ``` 或者使用AVX2指令集(假设数组元素为32位整数): ```cpp #include <immintrin.h> void parallel_access(int* in, int* out, int increment, int n) { // 每次处理8个整数(AVX2有256位寄存器,256/32=8) __m256i inc_vec = _mm256_set1_epi32(increment); for (int i = 0; i < n; i += 8) { // 加载8个整数 __m256i data = _mm256_loadu_si256((__m256i*)&in[i]); // 向量加法 __m256i result = _mm256_add_epi32(data, inc_vec); // 存储结果 _mm256_storeu_si256((__m256i*)&out[i], result); } } ``` 总结:同时访问数组的所有元素需要并行处理。在FPGA上,我们通过循环展开和数组分区来实现;在CPU上,我们可以使用多线程或SIMD指令。 根据你的需求,请选择适合的方法。 如果你有特定的环境(如HLS、OpenMP、CUDA等)或数组大小,我可以提供更具体的代码。 §§相关问题§§: 1. 在Vivado HLS中,完全展开循环(full unroll)对资源有什么影响? 2. 如何使用OpenMP实现数组的并行初始化? 3. 在AVX2指令集中,如何处理不对齐的内存访问? 4. 在FPGA上,如果数组太大无法完全分区,有哪些替代方案来提高并行度?</think>### 同时访问数组所有数据的解决方案 在硬件加速(尤其是 FPGA)中,同时访问数组的所有数据需要特殊优化技术。以下是实现方案: #### 核心方法:完全分区 (Complete Partitioning) ```cpp #define ARRAY_SIZE 64 // 示例数组大小 typedef ap_fixed<16,8> data_t; // 16位定点数(8位整数+8位小数) // 声明数组并应用完全分区 data_t my_array[ARRAY_SIZE]; #pragma HLS ARRAY_PARTITION variable=my_array complete dim=1 ``` #### 完整并行访问示例 ```cpp #include "ap_fixed.h" #include "hls_vector.h" #define SIZE 32 void parallel_array_processing( hls::vector<ap_fixed<16,8>, SIZE> &input, hls::vector<ap_fixed<16,8>, SIZE> &output ) { #pragma HLS INTERFACE mode=ap_memory port=input #pragma HLS INTERFACE mode=ap_memory port=output // 1. 声明并完全分区数组 static ap_fixed<16,8> buffer[SIZE]; #pragma HLS ARRAY_PARTITION variable=buffer complete dim=1 // 2. 同时写入所有数据 WRITE_LOOP: for (int i = 0; i < SIZE; i++) { #pragma HLS UNROLL // 完全展开循环 buffer[i] = input[i]; } // 3. 并行处理所有元素(示例:向量加法) PROCESS_LOOP: for (int i = 0; i < SIZE; i++) { #pragma HLS UNROLL buffer[i] = buffer[i] + 0.5; // 所有元素同时加0.5 } // 4. 同时读取所有数据 READ_LOOP: for (int i = 0; i < SIZE; i++) { #pragma HLS UNROLL output[i] = buffer[i]; } } ``` ### 关键优化技术详解 1. **完全数组分区 (`complete dim=1`)** - 将数组拆分为独立的寄存器 - 每个元素都有专用存储和访问路径 - 效果:所有元素可同时读写 2. **循环完全展开 (`UNROLL`)** - 消除循环结构 - 创建并行硬件电路 - 效果:所有操作在单周期内完成 3. **HLS向量接口 (`hls::vector`)** - 宽总线传输(示例中为32元素x16位=512位总线) - 单周期完成整个数组传输 - 替代方案:`#pragma HLS INTERFACE mode=ap_fifo` 流接口 4. **资源利用分析(SIZE=32时)** ```plaintext | 资源类型 | 使用量 | FPGA占比 | |------------|---------|----------| | 寄存器 | 512 | 0.5% | | LUT | 1024 | 1.2% | | 时钟周期 | 3 | (固定) | ``` - 小数组(<64元素)适合此方案 - 大数组需考虑资源约束 ### 替代方案:大数组处理技术 当数组太大无法完全分区时: ```cpp #define LARGE_SIZE 1024 void block_processing() { ap_fixed<16,8> big_array[LARGE_SIZE]; // 块分区 + 部分展开 #pragma HLS ARRAY_PARTITION variable=big_array block factor=32 dim=1 for (int i = 0; i < LARGE_SIZE; i++) { #pragma HLS PIPELINE II=1 #pragma HLS UNROLL factor=32 // 每次处理32个元素 big_array[i] = process(big_array[i]); } } ``` ### 性能对比 | 方法 | 访问延迟 | 吞吐量 | 资源消耗 | 适用场景 | |---------------------|----------|------------|----------|---------------| | 完全分区 (Complete) | 1周期 | 1周期/全量 | 极高 | 小数组(<64) | | 块分区 (Block) | 1周期 | N/factor | 中等 | 中等数组 | | 循环分区 (Cyclic) | 1周期 | N/factor | 中等 | 流式访问 | | 无优化 | N周期 | 1元素/周期 | 极低 | 资源受限系统 | > **最佳实践**:根据数组大小和目标频率选择策略: > - < 64 元素:完全分区 > - 64-512 元素:块/循环分区 + 部分展开 > - >512 元素:内存分块访问 + 流水线 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值