Skip to main content
1 of 8
xzczd
  • 68.4k
  • 9
  • 174
  • 489

Can I generate a "piecewise" list from a list in a fast and elegant way?

Suppose we have a number list a:

n = 1000000;
a = RandomReal[{0, 1}, {n}];

We want to generate a list c with the same dimension of a and the relationship between the elements of a and c is:

$c[[i]] == 2 a[[i]] +3$

I think there will be no doubt that the best way to get this c is:

c = 2 a + 3

However, what if c is a "piecewise" list i.e. elements in different part of c are generated by different formula? For example, n/2 of the elements right in the middle of c are 0. while others still follow the formula above.

The most direct way I can think out is:

c1 = Table[If[n/4 + 1 <= i <= 3 n/4, 0., 2 a[[i]] + 3 ], {i, n}]; // AbsoluteTiming

{7.7380000, Null}

It's simple, but too slow.

While two approaches are fast:

(c2 = 2 a + 3 ; 
  c2[[n/4 + 1 ;; 3 n/4]] = ConstantArray[0., {n/2}];) // AbsoluteTiming
(c3 = ConstantArray[0., {n}];
  c3[[1 ;; n/4]] = 2 a[[1 ;; n/4]] + 3 ; 
  c3[[3 n/4 + 1 ;; -1]] = 2 a[[3 n/4 + 1 ;; -1]] + 3;) // AbsoluteTiming

{0.0760000, Null} {0.0580000, Null}

But they are so dirty… And things will be worse when the dimension of the list becomes higher:

n=100;
b = RandomReal[{0, 1}, {n, n, n}]

d1 = Table[
    If[n/4 + 1 <= i <= 3 n/4 && n/4 + 1 <= j <= 3 n/4 && 
      n/4 + 1 <= k <= 3 n/4, 0., 2 b[[i, j, k]] + 3 ], {i, n}, {j, 
     n}, {k, n}]; // AbsoluteTiming
(d3 = ConstantArray[0, {n, n, n}]; 
   d3[[1 ;; n/4]] = 2 b[[1 ;; n/4]] + 3; 
   d3[[3 n/4 + 1 ;; -1]] = 2 b[[3 n/4 + 1 ;; -1]] + 3;
   d3[[n/4 + 1 ;; 3 n/4, 1 ;; n/4]] = 
    2 b[[n/4 + 1 ;; 3 n/4, 1 ;; n/4]] + 3; 
   d3[[n/4 + 1 ;; 3 n/4, 3 n/4 + 1 ;; -1]] = 
    2 b[[n/4 + 1 ;; 3 n/4, 3 n/4 + 1 ;; -1]] + 3;
   d3[[n/4 + 1 ;; 3 n/4, n/4 + 1 ;; 3 n/4, 1 ;; n/4]] = 
    2 b[[n/4 + 1 ;; 3 n/4, n/4 + 1 ;; 3 n/4, 1 ;; n/4]] + 3; 
   d3[[n/4 + 1 ;; 3 n/4, n/4 + 1 ;; 3 n/4, 3 n/4 + 1 ;; -1]] = 
    2 b[[n/4 + 1 ;; 3 n/4, n/4 + 1 ;; 3 n/4, 3 n/4 + 1 ;; -1]] + 3;); // AbsoluteTiming

{12.8440000, Null} {0.1990000, Null}

And a nightmare when the region are irregular:

f1 = Table[If[i^2 + j^2 + k^2 <= n^2, 0., 2 b[[i, j, k]] + 3 ], 
           {i, n}, {j, n}, {k, n}]; // AbsoluteTiming

{7.4530000, Null}

A fast f3 with the method shown in c3 and d3 is of course possible, but I'd like to skip it now.

So my question is, as the title said, is there an approach that's both elegant as c1 and fast as c3? Or at least not that slow as c1 and not that dirty as c3? Or, I can't have my cake and eat it too?

I add the tag "difference-equations" because this question is in fact generated when I explore finite difference method.

xzczd
  • 68.4k
  • 9
  • 174
  • 489