3

I want to populate an array of size n like this (a very big array):

1 for i = 1, (n/2)+1
2 for i = 2, 3, ... , (n/2)
0 for i = (n/2)+2, ... , n

Is the fastest way iterating through 0 to n and using an if statement and % for each one?

Like this:

array = []
for index in range(1,n):
    if index == 1 or (index % ((n/2)+1) == 0):
        array.append(1)
    if index == 2 or index == 3 or (index % (n/2) == 0):
        array.append(2)
    if (index % ((n/2)+2) == 0):
        array.append(0)

I have tried to think of other ways to do this but I have not come up with anything. I'm not a programmer by trade but I am not sure how else I would implement this.

Brandan B
  • 464
  • 2
  • 6
  • 21
  • None of those will work. You can't create list elements by assigning to the index, you have to use `array.append()`. – Barmar May 23 '19 at 20:35
  • In general, no, looping through to set each item is not the fastest. A few things though first: (1) what is `u`? (2) How are you handling odd n? – Jake Stevens-Haas May 23 '19 at 20:35
  • BTW, `[]` creates a list, not an array. Python only has arrays in modules like `numpy`. – Barmar May 23 '19 at 20:35
  • What is `i`, is it supposed to be the same as `index`? – Barmar May 23 '19 at 20:36
  • Sorry "i" is supposed to be index; im supposed to use array.append; len(u) = n – Brandan B May 23 '19 at 20:37
  • What should the value be for `i = 0`? – Barmar May 23 '19 at 20:43
  • Your requirements are very unclear. Nothing in the original requirements suggests the use of `mod`, but you use that in your attempted solution. It makes me think you don't understand how that operator works. – Barmar May 23 '19 at 20:44
  • If you want to iterate from 1 to n, you need to use `range(1, n+1)`, since `range()` doesn't include the last value. – Barmar May 23 '19 at 20:46
  • But your text says from 0 to n, not 1 to n. This question is really confusing, are you sure you understand the requirements? Perhaps you should show an actual example of the desired results. – Barmar May 23 '19 at 20:47

4 Answers4

1

You might want to use numpy for this. You can use np.r_ to concatenate multiple slices and use them to slice assign the array:

import numpy as np

n = 10
a = np.zeros(n)
a[np.r_[0,n//2]] = 1
a[np.r_[1:n//2]] = 2
a[np.r_[n//2+1:n]] = 0

print(a)
array([1., 2., 2., 2., 2., 1., 0., 0., 0., 0.])
yatu
  • 86,083
  • 12
  • 84
  • 139
1

Since the other answers went with numpy arrays, which are pretty fast, I want to add some other answers for completeness. (There's obviously some ambiguity in the original question about what to do if n is odd and whether the author was expecting zero-based or one-based indexing.)

You can create a list pretty simply using a list comprehension

[1 if i in [0, n/2] else (2 if i <=(n/2) else 0) for i in range(0, n)]

But way quicker is extending the functionality of a list by extending the class:

class NewThing(list):
    def __init__(self, n):
        self.n = n
    def __getitem__(self,i):
        if i in {0,n/2}:
            return 1
        elif i <=n/2:
            return 2
        return 0
    def to_list(self):
        return [self[i] for i in range(0, n)]

>>>unique_list = NewThing(len(u))
>>>unique_list[4]
2
>>>unique_list.to_list()
[1, 2, 2, 2, 2, 1, 0, 0, 0, 0]

You can call for the whole list, or just individual elements. Creating the whole list is still slow.

Jake Stevens-Haas
  • 1,186
  • 2
  • 14
  • 26
1

The most efficient way to construct such a collection, is with a numpy array.

We can construct this with:

import numpy as np

def generate_array(n):
    a = np.hstack((np.full(n//2+1, 2), np.zeros(n//2-1)))
    a[[0, n//2]] = 1
    return a

For example:

>>> generate_array(6)
array([1., 2., 2., 1., 0., 0.])
>>> generate_array(10)
array([1., 2., 2., 2., 2., 1., 0., 0., 0., 0.])
>>> generate_array(16)
array([1., 2., 2., 2., 2., 2., 2., 2., 1., 0., 0., 0., 0., 0., 0., 0.])

Timings:

for n=100_000, we get:

>>> timeit(partial(generate_array, 100_000), number=10_000)
1.0764452270013862

for n=1_000_000, we get:

>>> timeit(partial(generate_array, 1_000_000), number=1_000)
6.448311180000019

Generating an array of 100k elements, thus takes approximately (including small overhead of partial, etc.) 107.64 µs, generating an array of 1M elements takes 6448.31µs.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
1

for even n:

def generate_array2(n):
    a = np.empty((2,n//2))
    a[0]=2
    a[1]=0
    a[:,0]=1
    return a.ravel()

%timeit a= generate_array2(1_000_000)
491 ± 6.58 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
B. M.
  • 18,243
  • 2
  • 35
  • 54