折叠纸带并在展开时生成数字

时间:2016-04-26 00:54:26

标签: java algorithm recursion data-structures

我有一个我正在努力的任务问题,需要一些方向来解决。 假设我有一张纸条,我将它从中心折叠,使左半部分落在右半部分后面。然后我按顺序编号折叠的peices,当我展开如下时,我得到数字。 1:2

如果我折叠两次,我会按照以下方式展开数字 1:4:3:2

如果我三次折叠,我得到如下 1 8 5 4 3 6 7 2

当我折叠n次时,我想生成数字数组。所以,如果我折叠例如25次,我会得到2 ^ 25个相似的序列号。

这些是我所做的观察

  • 第一个和最后一个数字始终为1和2.

  • 中间的两个数字总是4和3

  • 索引1处的数字是最大数字,倒数第二位置的数字是第二大数字。

  • 它看起来像二进制搜索树的前序遍历,但我不知道它有多大帮助。

  • 我尝试从预订中构建二叉树,然后将其转换为有序,假设我可以将此过程转换为相同的系列而我错了。

编辑:为了搜索此生成的数组中的元素,我可以进行顺序搜索,这将是O(n)效率。但我意识到在这个系列中搜索一个数字必须有一个更快的方法。

我不能进行二分搜索,因为它没有排序,当完成25次折叠时,有超过十亿的数字。

我可以使用哪种搜索策略来查找数字及其索引?

这是我想将其转换为具有log(n)搜索效率的二叉搜索树的原因之一。

编辑2:我尝试了其中一个答案所建议的表格折叠算法,但它不具有内存效率。我不能在我的记忆中存储超过十亿个数字,因此必须有一种方法来查找数字索引而不实际创建数字数组。

4 个答案:

答案 0 :(得分:6)

第1折:1 2

第二次折叠:1 4 3 2

第3折:1 8 5 4 3 6 7 2

第4折:1 16 9 8 5 12 13 4 3 14 11 6 7 10 15 2

生成表格(示例为第4折)

  

想象一下,你有第n张纸然后展开它。

  1. 生成一个包含大小的表(列= 1,行= 2 ^ n)并从下向上填充列,并按升序排列值

    16
    15
    14
    13
    12
    11
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    
  2. 通过从前到后将顶部x行粘贴到底部x行来递归调整表的大小(column = org.column * 2,row = org.row / 2)

    8  9
    7 10
    6 11
    5 12
    4 13
    3 14
    2 15
    1 16  
    
    4 13 12  5
    3 14 11  6
    2 15 10  7
    1 16  9  8   
    
    2 15 10  7  6 11 14  3
    1 16  9  8  5 12 13  4
    
    1 16  9  8  5 12 13  4  3 14 11  6  7 10 15  2
    
  3. 从结尾读取最后一行表格

      

    1 16 9 8 5 12 13 4 3 14 11 6 7 10 15 2

  4. 剩下的工作是证明这项工作然后编码(我只测试n = 4,因为我很懒)

答案 1 :(得分:5)

您可以计算折叠的数量,而无需通过使用位反转来计算整个序列(这会颠倒数字的二进制表示,以便例如0001变为1000)。< / p>

这些是你通过位反转获得的序列:

1 bit:    0  1
2 bits:   0  2  1  3
3 bits:   0  4  2  6  1  5  3  7
4 bits:   0  8  4 12  2 10  6 14  1  9  5 13  3 11  7 15

这些是纸张折叠序列(从0开始计算):

1 fold:   0  1
2 folds:  0  3  2  1
3 folds:  0  7  4  3  2  5  6  1
4 folds:  0 15  8  7  4 11 12  3  2 13 10  5  6  9 14  1

如果将纸张折叠序列分成偶数和奇数,则得到:

          0
             1
          0     2
             3     1
          0     4     2     6
             7     3     5     1
          0     8     4    12     2    10     6    14
            15     7    11     3    13     5     9     1

您将看到纸张折叠序列与位反转序列相同,但前半部分(偶数)与后半部分(奇数)相反。

您还会注意到,每对相邻的偶数/奇数加起来为2 n -1(其中n是折叠数),这意味着它们是彼此&# 39; s反向,你可以使用逐位NOT来计算另一个。

因此,要将折叠的折叠数x(从0开始计算)折叠n次:

  

将x除以2,如果x为奇数则执行按位NOT,然后按位反转(使用n位数)

示例(折叠4次):

fold   x/2    binary   inverted    bit-reversed    from 1

 0      0      0000                0000       0       1
 1      0      0000      1111      1111      15      16
 2      1      0001                1000       8       9
 3      1      0001      1110      0111       7       8
 4      2      0010                0100       4       5
 5      2      0010      1101      1011      11      12
 6      3      0011                1100      12      13
 7      3      0011      1100      0011       3       4
 8      4      0100                0010       2       3
 9      4      0100      1011      1101      13      14
10      5      0101                1010      10      11
11      5      0101      1010      0101       5       6
12      6      0110                0110       6       7
13      6      0110      1001      1001       9      10
14      7      0111                1110      14      15
15      7      0111      1000      0001       1       2

示例:第十亿折:(折叠30次)

fold:            1,000,000,000
counting from 0:   999,999,999 (x is odd)
x/2:               499,999,999
binary:          011101110011010110010011111111 (30 digits)
bitwise NOT:     100010001100101001101100000000 (because x was odd)
bit-reversed:    000000001101100101001100010001
decimal:             3,560,209
counting from 1:     3,560,210

我不会说Java,但是这样的事情应该可以解决问题:

public static long foldIndex(int n, long x) { // counting from zero
    return Long.reverse((x & 1) == 0 ? x >>> 1 : ~(x >>> 1)) >>> (Long.SIZE - n);
}

答案 2 :(得分:2)

  

这是一种算法,用于查找数字之后的数字索引   展开。

它会根据折叠情况跟踪搜索号码移动到的位置的坐标。例如,如果您对3折(n = 3,numFolds)感兴趣并且想知道数字7的位置(searchNumber),则算法运行如下:

Initial State:
8
7
6
5
4
3
2
1 

The 7 is at [1,7] - column 1, row 7 

Now, when we fold the top half down:
4 5
3 6
2 7
1 8

The 7 is at [2, 1] - column 2, row 2 

When we do the next fold the 7 does not move (hence the if (row > half) logic)

2 7 6 3
1 8 5 4

On the last fold:
1 8 5 4 3 6 7 2

The 7 is at [7, 1] - column 7, row 1 and the code will return 7. 


public static long getIndexOfAfterFold (long numFolds, long searchNumber)
{
    long total = (long) Math.pow(2, numFolds);

    long [] coordsOfSearchNumber = new long [] {1, searchNumber};

    int iterations = 0;

    while (iterations < numFolds)
    {
        long half = total / 2;

        long row = coordsOfSearchNumber[1];

        // we are folding down
        if (row > half)
        {
            long newRow = (total - row) + 1;

            long col = coordsOfSearchNumber[0];

            long newFoldThickness = (long) Math.pow(2, iterations + 1);
            long newCol =  newFoldThickness - (col - 1);

            coordsOfSearchNumber[0] = newCol;
            coordsOfSearchNumber[1] = newRow;
        }

        total = total / 2;

        iterations++;
    }       
    return coordsOfSearchNumber[0];
}

编辑:将上述代码转换为使用long代替int

备注

  • 它在O(n)时间运行,其中n是折叠数。
  • 用法:System.out.println(getIndexOfAfterFold(4, 13));

此代码将提供折叠后所有数字的列表

  

注意:这是基于@ hk6279(表格折叠算法)提供的答案

public static void unFold (int numFolds)
{
    int total = (int) Math.pow(2, numFolds);

    List<ArrayList<Integer>> table = new ArrayList<ArrayList<Integer>> (total);

    // populate the single column table
    for (int i = 0; i < total; i++)
    {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(i + 1);
        table.add(list);
    }

    int iterations = 0;

    while (iterations < numFolds)
    {
        int half = table.size() / 2;

        // place the fold back on itself
        for (int i = 0; i < half; i++)
        {
            ArrayList<Integer> list = table.get(i);

            ArrayList<Integer> foldList = table.get(table.size() - (i + 1));

            // reverse the fold
            Collections.reverse(foldList);

            // add the fold to front
            list.addAll(foldList);
        }

        // remove the part we folded
        table.subList(half, table.size()).clear();

        iterations++;
    }
    System.out.println(table);
}

这就是n = 5的样子:

1,32,17,16,9,24,25,8,5,28,21,12,13,20,29,4,3,30,19,14,11,22,27,6 ,7,26,23,10,15,18,31,2

答案 3 :(得分:0)

我不了解Java,但这应该很容易移植并适用于任意数量的折叠。想法和m69一样,所以我不会自己解释逻辑。

#include <iostream>

size_t reverse(size_t n, int bits)
{
    size_t result = 0;
    size_t msb_value = 1 << (bits - 1);
    while (n)
    {
        if (n & 1) result |= msb_value;
        msb_value >>= 1;
        n >>= 1;
    }
    return result;
}

struct Fold_Sequence
{
    Fold_Sequence(size_t folds) : folds_(folds), max_(1 << folds) { }

    size_t operator[](size_t i) const
    {
        size_t x = reverse((i / 2) % max_, folds_);
        return i & 1 ? (max_ - x - 1) : x;
    }

    size_t folds_, max_, i = 0;
};

int main()
{
    const size_t folds = 4;
    const unsigned num_parts = 1 << folds;
    Fold_Sequence seq{folds};
    for (unsigned j = 0; j < num_parts; ++j)
        std::cout << seq[j] + 1 << '\n';
}

我也喜欢hk6279解决方案的优雅,所以我实现了它(也是在C ++中,我懒得使用多维数组/ vector<vector<>>并且必须小心地调整大小,因此使用map键入x,y坐标来实现效率低下:

#include <iostream>
#include <map>

#define DBG(X) do { std::cout << X << '\n'; } while (false)

typedef std::pair<size_t, size_t> Coord;

struct matrix : std::map<Coord, size_t>
{
    matrix(size_t n)
      : y_size_(n)
    {
        for (size_t i = 0; i < n; ++i)
            (*this)[{0, i}] = i;  // bottom left is 0,0; 0,1 is above
    }

    void fold()
    {
        size_t x_size_ = x_size();
        for (size_t y = y_size_ / 2; y < y_size_; ++y)
            for (size_t x = 0; x < x_size_; ++x)
                move(x, y, x_size_ * 2 - x - 1, y_size_ - y - 1);
        y_size_ /= 2;
    }

    void move(size_t from_x, size_t from_y, size_t to_x, size_t to_y)
    {
        DBG("move(" << from_x << ',' << from_y << " -> " << to_x << ',' << to_y
            << ") value " << ((*this)[{from_x, from_y}]));
        (*this)[{to_x, to_y}] = (*this)[{from_x, from_y}];
        erase({from_x, from_y});
    }

    size_t operator()(size_t x, size_t y) const
    {
        auto it = find({x, y});
        if (it != end()) return it->second;
        std::cerr << "m(" << x << ',' << y << ") doesn't exist\n";
        exit(1);
    }

    size_t x_size() const { return size() / y_size_; }
    size_t y_size() const { return y_size_; }

    size_t y_size_;
};

std::ostream& operator<<(std::ostream& os, const matrix& m)
{
    for (size_t y = m.y_size_ - 1; y <= m.y_size_; --y)
    {
        for (size_t x = 0; x < m.x_size(); ++x)
            os << m(x, y) << ' ';
        os << '\n';
    }
    return os;
}

int main()
{
    const size_t n = 4;
    matrix m(1 << n);
    for (int i = 0; i < n; ++i)
    {
        m.fold();
        std::cout << i+1 << " folds ==> " << m.x_size() << 'x' << m.y_size()
            << " matrix:\n" << m << '\n';
    }
}