24-point expansion-arbitrary majority gets a new number through four arithmetic operations (n number n points, matlab program)

24-point expansion-arbitrary majority gets a new number through four arithmetic operations (n ​​number n points, matlab program)

Water one. Since the parent group often post various "puzzle" questions, such as how to make 4 6 into 5, how to get 20 from 123456, etc., so I took the trouble to write a program once and for all.

1 general idea

Take the following question as an example:
1 2 3, how to add addition, subtraction, multiplication, division and parentheses to get 4.
(To add exponentiation, you can add the ^ symbol. I am afraid that the computer will explode, so I dare not add it)

The correct answer is:
2*(3-1)=4
(3+2)-1=4
. . . etc.

Observe the equation, which contains three categories of numbers, symbols and operation order. By changing these three categories, we can get different permutations and combinations

insert image description here
As shown in the figure above, where N represents the numbers involved in the arrangement, there are three 123 in the figure, so N=3. Among them, there are N! types of permutations of numbers, which increase at the rate of factorial. There are 4^(N-1) types of symbol changes, growing exponentially. And the calculation order has (N-1)!, which is also increasing at the rate of factorial.

Therefore, if we brute force all combinations, we have:
N ! ∗ 4 N − 1 ∗ ( N − 1 ) ! N!*4^{N-1}*(N-1)!N!4N1(N1 ) !
When N is equal to 3, only 192 calculations are required;
when N is 4, 9216 calculations are required;
when N is 5, 737280 calculations are required.
This explosive growth makes it very slow to calculate data with a large amount of data.

So it goes without saying that the disadvantage of this algorithm is that it is too computationally intensive. Moreover, from the algorithm design itself, it involves 3-layer loop calculation, which is very unfriendly to matlab and will lead to slower speed. Moreover, the algorithm uses the method of calling the eval function by a string, and the eval function of matlab is relatively slow, so the efficiency is even worse. (It can't be saved, it can't be saved)

1.1 Number Arrangement Algorithm

Here, the function perms() in matlab is directly used for permutation and combination, including all possible sorts of input vectors.

1.2 Symbol sorting algorithm

Symbol sorting is a repeated full arrangement, and each symbol can appear repeatedly.
Here is the idea of ​​the following article:
How does matlab realize the problem of full combination of repeated numbers

The article uses the ndgrid function to generate non-repeating N-dimensional grids, and then uses the coordinates of each unique grid point as a fully arranged point.

1.3 Calculation sequence bracket algorithm

The parenthesis algorithm is performed in a step-by-step manner, taking the following figure as an example (the Mermaid flow chart cannot display parentheses, use <> instead of parentheses):

1 , 2 , 3 , 4
<1#2> , 3 , 4
1 , <2#3> , 4
1 , 2 , <3#4>
<<1#2>#3> , 4
<1#2> , <3#4>
<1#<2#3>> , 4
1 , <<2#3>#4>
<1#2> , <3#4>
1 , <2#<3#4>>
<<1#2>#3>#4
<1#2>#<3#4>
<1#<2#3>>#4
1#<<2#3>#4>
<1#2>#<3#4>
1#<2#<3#4>>4

It doesn't look good, so I redraw it again.
insert image description here
First, take 1 bracket every two times to merge and save it. After that, take 2 merges each time and save them. Until there is only one element left in all the lists, it means that the merger is completed and all the parentheses are obtained.

2 program code

%24点
clear
clc
%初始条件
Number=[6 6 6 6];
Result=24;
N=numel(Number);
Symbol={'+','-','*','/'};
N_Symbol=1:numel(Symbol);
%生成列表
Number_List = perms(Number);%生成数字排列
Number_List=unique(Number_List,'rows');
N_Symbol_List = Full_Choose(N_Symbol,N-1);%生成符号排列
P_List = Full_Parenthese_List(N);%生成括号排列
%生成替换符
S_Replace=cell(1,N-1);%创建符号替换符
for k=1:N-1
    S_Replace{k}=['s',num2str(k,'%02.0f')];
end
N_Replace=cell(1,N);%创建数字替换符
for k=1:N
    N_Replace{k}=['x',num2str(k,'%02.0f')];
end

%定义结果cell
Result_List={};
m=1;
f=waitbar(0,'进度条');
N_Sum=size(P_List,1)*size(N_Symbol_List,1);
m2=1;
%进行数字、符号、括号的三层循环
for x=1:size(P_List,1)
    %括号
    Str_Temp1=P_List{x,1};
    for y=1:size(N_Symbol_List,1)
        %符号
        S_L_temp=N_Symbol_List(y,:);
        S_Replace_New=Symbol(S_L_temp);%生成替换的符号
        Str_Temp2=replace(Str_Temp1,S_Replace,S_Replace_New);
        for z=1:size(Number_List,1)
            N_Replace_New=string( Number_List(z,:) );
            Str_Temp3=replace(Str_Temp2,N_Replace,N_Replace_New);
            X=eval([Str_Temp3]);
            if abs(X-Result)<1e-5
                Result_List{m,1}=[Str_Temp3,'=',num2str(Result)];
                m=m+1;
            end
        end
        %进度条
        waitbar(m2/N_Sum,f,'进度条');
        m2=m2+1;
    end
end

close(f);
%删除重复结果
Result_List=unique(Result_List);%删除重复元素
%输出
Result_List





function FullList=Full_Choose(n_List,n)
%全排列,可以重复
%n_List元素
%n是全排列的位数
V_Str='Xgrid';
V_Str_Sum='';
%思路是利用ndgrid方法,实现全排列。用eval函数执行
for k=1:n
    V_Str_Sum=[V_Str_Sum,V_Str,num2str(k),','];
end
V_Str_Sum(end)=' ';
Eval_Str=['[',V_Str_Sum,']=ndgrid(','n_List',');'];
eval(Eval_Str);
V_Str_Sum2=[];
for k=n:-1:1
    V_Str_Sum2=[V_Str_Sum2,V_Str,num2str(k),'(:),'];
end
V_Str_Sum2(end)=' ';
Eval_Str=['FullList=[',V_Str_Sum2,'];'];
eval(Eval_Str);

FullList=n_List(FullList);%把生成好的列表带入到nlist中
FullList=unique(FullList,'rows');
end

function NL=Full_Plus(N)
%将一个整数N输出为若干整数相加的形式
NL_i=ones(1,N);
NL=[];
NL=[NL;NL_i];%总的列表
%m=sum(NL_i>0);
NL_k=NL_i;%上一步循环中列表的行数
for k=2:N
    %循环,每次循环k内生成的元素,比上一次循环减一
    NL_k2=[];
    for j=1:size(NL_k,1)
        NL_i=NL_k(j,:);%从上一步中结果抽取一个
        NL_k_temp=Plus2List(NL_i);%执行两两相加,输出结果
        NL_k2=[NL_k2;NL_k_temp];
    end
    
    NL_k2=unique(NL_k2,'rows');%删除重复元素
    NL_k=NL_k2;
    NL=[NL;NL_k];%加入总的列表
end

end

function NL_k=Plus2List(NL_i)
%输入1个行向量,输出所有两两相加的和
N=numel(NL_i);%行向量的元素
NL_i_num=sum(NL_i>0);
Plus_List=nchoosek(1:NL_i_num,2);%需要两两相加的列表
N_Plus=size(Plus_List,1);%计算循环次数
NL_k=zeros(N_Plus,N);%初始化输出矩阵
for k=1:N_Plus
    NL_temp=NL_i;%创建临时向量
    NL_temp(Plus_List(k,1))=NL_temp(Plus_List(k,1))+NL_temp(Plus_List(k,2));
    NL_temp(Plus_List(k,2))=0;
    NL_temp=sort(NL_temp,'descend');
    NL_k(k,:)=NL_temp;
end
NL_k=unique(NL_k,'rows');%删除重复元素
end

function P_L_1=Full_Parenthese_List(N)
%生成括号组合,采用两两合并的思想
P_L_1=cell(1,N);%创建元素
for k=1:N
    P_L_1{k}=['x',num2str(k,'%02.0f')];
end
%生成括组合
for k=1:N-1
    P_L_2={};
    for j=1:size(P_L_1,1)
        P_L_temp1=P_L_1(j,:);
        P_L_temp2=Combine_Parenthese(P_L_temp1);
        P_L_2=[P_L_2;P_L_temp2];
    end
    P_L_1=P_L_2;
end
%去掉收尾多余的括号
for k=1:size(P_L_1,1)
    str_temp=P_L_1{k};
    str_temp(end)=[];
    str_temp(1)=[];
    P_L_1{k}=str_temp;
end
%把加号替换为's1','s2'...
for k=1:size(P_L_1,1)
    str_temp=P_L_1{k};
    idx = strfind(str_temp,'+++');
    for j=numel(idx):-1:1
        str_temp(idx(j):idx(j)+2)=['s',num2str(j,'%02.0f')];
    end
    P_L_1{k}=str_temp;
end

end


function P_L_2=Combine_Parenthese(P_L_1)
%把PL1中的元素两两组合
N=numel(P_L_1);
P_L_2=cell(N-1,N-1);
for k=1:size(P_L_2,1)
    for j=1:size(P_L_2,2)
        if j==k
            P_L_2(k,j)={ ['(',P_L_1{1,j},'+++',P_L_1{1,j+1},')'] };%用+++占位,作为符号
        elseif j>k
            P_L_2(k,j)={ P_L_1{1,j+1} };
        elseif j<k
            P_L_2(k,j)={ P_L_1{1,j} };
        end
    end
end
end

Take the five numbers [1,2,3,4,5] as an example, the requirement is equal to 78, and the output result is:

{'(((1+3)*4)*5)-2=78'}
    {'(((1+3)*5)*4)-2=78'}
    {'(((1+5)*4)+2)*3=78'}
    {'(((3+1)*4)*5)-2=78'}
    {'(((3+1)*5)*4)-2=78'}
    {'(((5+1)*4)+2)*3=78'}
    {'((1+3)*(4*5))-2=78'}
    {'((1+3)*(5*4))-2=78'}
    {'((3+1)*(4*5))-2=78'}
    {'((3+1)*(5*4))-2=78'}
    {'((4*(1+3))*5)-2=78'}
    {'((4*(1+5))+2)*3=78'}
    {'((4*(3+1))*5)-2=78'}
    {'((4*(5+1))+2)*3=78'}
    {'((4*5)*(1+3))-2=78'}
    {'((4*5)*(3+1))-2=78'}
    {'((5*(1+3))*4)-2=78'}
    {'((5*(3+1))*4)-2=78'}
    {'((5*4)*(1+3))-2=78'}
    {'((5*4)*(3+1))-2=78'}
    {'(2+((1+5)*4))*3=78'}
    {'(2+((5+1)*4))*3=78'}
    {'(2+(4*(1+5)))*3=78'}
    {'(2+(4*(5+1)))*3=78'}
    {'(4*((1+3)*5))-2=78'}
    {'(4*((3+1)*5))-2=78'}
    {'(4*(5*(1+3)))-2=78'}
    {'(4*(5*(3+1)))-2=78'}
    {'(5*((1+3)*4))-2=78'}
    {'(5*((3+1)*4))-2=78'}
    {'(5*(4*(1+3)))-2=78'}
    {'(5*(4*(3+1)))-2=78'}
    {'3*(((1+5)*4)+2)=78'}
    {'3*(((5+1)*4)+2)=78'}
    {'3*((4*(1+5))+2)=78'}
    {'3*((4*(5+1))+2)=78'}
    {'3*(2+((1+5)*4))=78'}
    {'3*(2+((5+1)*4))=78'}
    {'3*(2+(4*(1+5)))=78'}
    {'3*(2+(4*(5+1)))=78'}

It can be seen that the basic results are only 4*20-2, ((6 *4)+2) *3, and there are many results that can be eliminated by the commutative law of addition or multiplication. This is also a deficiency that needs to be improved.

3 Update the anonymous function method

The previous eval function was very slow, and I was thinking about whether there is any alternative. So an anonymous function is used to replace it.

But it's actually not that much faster. Here is the algorithm:

%24点
clear
clc
%初始条件
Number=[1 2 3 4 5 6];
Result=91;
N=numel(Number);
Symbol={'+','-','*','/'};
S{1}=@(a,b) a+b;
S{2}=@(a,b) a-b;
S{3}=@(a,b) a*b;
S{4}=@(a,b) a/b;
N_Symbol=1:numel(Symbol);
%生成列表
Number_List = perms(Number);%生成数字排列
Number_List=unique(Number_List,'rows');
N_Symbol_List = Full_Choose(N_Symbol,N-1);%生成符号排列
P_List = Full_Parenthese_List(N);%生成括号排列
PN_List = CycleNum(N-1:-1:1);
PN_Symbol_List = sortrows(perms(1:(N-1)));%生成符号顺序替换排列
%生成替换符
S_Replace=cell(1,N-1);%创建符号替换符
for k=1:N-1
    S_Replace{k}=['s',num2str(k,'%02.0f')];
end
N_Replace=cell(1,N);%创建数字替换符
for k=1:N
    N_Replace{k}=['x',num2str(k,'%02.0f')];
end

%定义结果cell
Result_List={};
m=1;
N_Sum=size(P_List,1)*size(N_Symbol_List,1);
%进行数字、符号、括号的三层循环
for x=1:size(P_List,1)
    t_start= cputime;
    %括号
    Str_Temp1=P_List{x,1};
    for y=1:size(N_Symbol_List,1)
        %符号
        S_L_temp=N_Symbol_List(y,:);
        S_Replace_New=Symbol(S_L_temp);%生成替换的符号
        Str_Temp2=replace(Str_Temp1,S_Replace,S_Replace_New);
        for z=1:size(Number_List,1)

            X1=Number_List(z,:);%数字列表,代表4个数字
            Y1=S_L_temp;%运算符,1加法,2减法,3乘法
            %运算顺序
            Z1=PN_List(x,:);
            Z2=PN_Symbol_List(x,:);
            
            %已知数字,运算符,运算顺序,计算结果
            Temp=X1(:)';
            for k=1:N-1
                Zk=Z1(k);
                Yk=Y1(Z2(k));%这里运算符顺序的定义和原版程序不同
                Temp2=[Temp(1:Zk-1),S{Yk}(Temp(Zk),Temp(Zk+1)),Temp(Zk+2:end)];
                Temp=Temp2;
            end
            
            if abs(Temp-Result)<1e-5
                N_Replace_New=string( Number_List(z,:) );
                Str_Temp3=replace(Str_Temp2,N_Replace,N_Replace_New);
                Result_List{m,1}=[Str_Temp3,'=',num2str(Result)];
                m=m+1;
            end
        end
    end
end

%删除重复结果
Result_List=unique(Result_List);%删除重复元素
disp(Result_List)

function FullList=Full_Choose(n_List,n)
%全排列,可以重复
%n_List元素
%n是全排列的位数
V_Str='Xgrid';
V_Str_Sum='';
%思路是利用ndgrid方法,实现全排列。用eval函数执行
for k=1:n
    V_Str_Sum=[V_Str_Sum,V_Str,num2str(k),','];
end
V_Str_Sum(end)=' ';
Eval_Str=['[',V_Str_Sum,']=ndgrid(','n_List',');'];
eval(Eval_Str);
V_Str_Sum2=[];
for k=n:-1:1
    V_Str_Sum2=[V_Str_Sum2,V_Str,num2str(k),'(:),'];
end
V_Str_Sum2(end)=' ';
Eval_Str=['FullList=[',V_Str_Sum2,'];'];
eval(Eval_Str);

FullList=n_List(FullList);%把生成好的列表带入到nlist中
FullList=unique(FullList,'rows');
end


function P_L_1=Full_Parenthese_List(N)
%生成括号组合,采用两两合并的思想
P_L_1=cell(1,N);%创建元素
for k=1:N
    P_L_1{k}=['x',num2str(k,'%02.0f')];
end
%生成括组合
for k=1:N-1
    P_L_2={};
    for j=1:size(P_L_1,1)
        P_L_temp1=P_L_1(j,:);
        P_L_temp2=Combine_Parenthese(P_L_temp1);
        P_L_2=[P_L_2;P_L_temp2];
    end
    P_L_1=P_L_2;
end
%去掉收尾多余的括号
for k=1:size(P_L_1,1)
    str_temp=P_L_1{k};
    str_temp(end)=[];
    str_temp(1)=[];
    P_L_1{k}=str_temp;
end
%把加号替换为's1','s2'...
for k=1:size(P_L_1,1)
    str_temp=P_L_1{k};
    idx = strfind(str_temp,'+++');
    for j=numel(idx):-1:1
        str_temp(idx(j):idx(j)+2)=['s',num2str(j,'%02.0f')];
    end
    P_L_1{k}=str_temp;
end
end

function P_L_2=Combine_Parenthese(P_L_1)
%把PL1中的元素两两组合
N=numel(P_L_1);
P_L_2=cell(N-1,N-1);
for k=1:size(P_L_2,1)
    for j=1:size(P_L_2,2)
        if j==k
            P_L_2(k,j)={ ['(',P_L_1{1,j},'+++',P_L_1{1,j+1},')'] };%用+++占位,作为符号
        elseif j>k
            P_L_2(k,j)={ P_L_1{1,j+1} };
        elseif j<k
            P_L_2(k,j)={ P_L_1{1,j} };
        end
    end
end
end


function L=CycleNum(X)
%按每一位顺序进行循环
%输入3,2
%生成[1,1][1,2][2,1][2,2][3,1][3,2]
N=prod(X);
M=length(X);
Np=[cumprod(X,2,'reverse'),1];
L=mod(fix((0:N-1)'*(1./Np(2:1+M))),X);
L=L+1;
end

Mainly the loop is too slow.

4 epilogue

Mainly to complain, no dry goods.
.
.
.

After I finished writing the program, some fresh questions were sent to the group:
1 1 1 1 = 5
9 9 9 9 = 5
So, I confidently entered the questions into the program, but found that there was no solution !

I was so scared that I quickly turned off the program, took out the paper and pen, and woke up my brain to solve the problem.
The first question, (1+1+1)!-1=5.
? ? ? When can factorial be added? If this is added to the program, wouldn't the computer explode if it is not careful?

The second question, ( 9 + 9 ) / 9 + 9 = 5 (9+9)/9+\sqrt{9}=5(9+9)/9+9 =5
? ? ? Isn't this cheating? The root sign itself hides the number 2. If you add the root sign randomly, you have to put it into the program. When is it a head?
.

Finally, a question about pressing the bottom of the box:
2 2 2 = 5
The answer is:
.
.
.
.
− log ⁡ 2 log ⁡ 2 2 = 5 -\log_{2}{\log_{2}{ \sqrt{ \sqrt{ \sqrt{ \sqrt{ \sqrt{2}}}}} }}=5log2log22 =5
? ? ? When did the logs come up, my god ORZ. This lets the computer guess. I can only say that the wisdom of workers is infinite, don't ask, it's a waste of a day to ask.

Guess you like

Origin blog.csdn.net/weixin_42943114/article/details/107325379