избежать вложенного цикла foreach в Java

java foreach nested processing-efficiency

428 просмотра

5 ответа

Прошло около 20 лет с тех пор, как я пошел на урок кодирования. Я поднимаю его время от времени для забавы, но мой код неуклюж и неэффективен.

У меня есть массив с чуть более 400 элементов. В настоящее время у меня есть вложенные циклы foreach, работающие над этим массивом.

import acm.program.ConsoleProgram;
import java.awt.Color;
import acm.io.IODialog;
import java.text.*;
import static java.lang.Math.*;
import java.util.*;

/** Tests to see if user color matches sample colors */

public class test extends ConsoleProgram

{

   //defining sample colors

   Color[] dmc =
   {
      new Color(255,255,255),
      new Color(148,91,128),
      new Color(206,148,186),
      new Color(236,207,225),
      new Color(243,218,228),

   };

 public void run()
 {
    average();

 }

 //averages three colors, then tests for match to given color

 public void average()
 {

    //asks for user color
    IODialog dialog = new IODialog();
    int stitchRed= dialog.readInt("Enter red value: ");
    int stitchGreen= dialog.readInt("Enter green value: ");
    int stitchBlue= dialog.readInt("Enter blue value: ");
    Color stitchColor= new Color(stitchRed,stitchGreen,stitchBlue);

    //gets averages for dmc colors
    for (Color i:dmc)
    {
       for (Color j:dmc)
       {
          for (Color k:dmc)
          {
              int indexI = Arrays.asList(dmc).indexOf(i);
              int indexJ = Arrays.asList(dmc).indexOf(j);
              int indexK = Arrays.asList(dmc).indexOf(k);
              if(indexI <= indexJ && indexJ <= indexK)
              {
                 int iRed = i.getRed();
                 int jRed = j.getRed();
                 int kRed = k.getRed();
                 int iGreen = i.getGreen();
                 int jGreen = j.getGreen();
                 int kGreen = k.getGreen();
                 int iBlue = i.getBlue();
                 int jBlue = j.getBlue();
                 int kBlue = k.getBlue();
                 int redAverage = (iRed+jRed+kRed)/3;
                 int greenAverage = (iGreen+jGreen+kGreen)/3;
                 int blueAverage = (iBlue+jBlue+kBlue)/3;
                 Color colorAverage = new Color(redAverage,greenAverage,blueAverage);

                 //tests to see if any thread average equals user color
                 if (colorAverage.equals(stitchColor))
                 {
                    println("The color match is: " + i + ", " + j + ", " + k);
                 }
            }
         }
       }
     }

     println("no more matches");
   }
 }

Это компилируется нормально, но работает очень медленно.

Есть ли более эффективный способ сделать это?
Возможно, способ обойти гнездование - что-то с эффектом:

для (Цвет i, j, k: dmc)

?

Автор: Molly Kitti Источник Размещён: 08.11.2019 11:04

Ответы (5)


1 плюс

Это равносильно ответу @ C0der, просто использует те же имена для var вопроса.

    for (int indexI = 0; indexI < dmc.length; indexI++) {
        Color i = dmc[indexI];
        for (int indexJ = indexI; indexJ< dmc.length; indexJ++) {
            Color j = dmc[indexJ];
            for (int indexK = indexJ; indexK< dmc.length; indexK++) {
                Color k = dmc[indexK];
                //....
            }
        }
    }

Отредактированный:

public void average() {
    //asks for user color
    IODialog dialog = new IODialog();
    int stitchRed = dialog.readInt("Enter red value: ");
    int stitchGreen = dialog.readInt("Enter green value: ");
    int stitchBlue = dialog.readInt("Enter blue value: ");
    Color stitchColor = new Color(stitchRed, stitchGreen, stitchBlue);
    for (int indexI = 0; indexI < dmc.length; indexI++) {
        Color i = dmc[indexI];
        for (int indexJ = indexI; indexJ < dmc.length; indexJ++) {
            Color j = dmc[indexJ];
            for (int indexK = indexJ; indexK < dmc.length; indexK++) {
                Color k = dmc[indexK];
                int iRed = i.getRed();
                int jRed = j.getRed();
                int kRed = k.getRed();
                int iGreen = i.getGreen();
                int jGreen = j.getGreen();
                int kGreen = k.getGreen();
                int iBlue = i.getBlue();
                int jBlue = j.getBlue();
                int kBlue = k.getBlue();
                int redAverage = (iRed + jRed + kRed) / 3;
                int greenAverage = (iGreen + jGreen + kGreen) / 3;
                int blueAverage = (iBlue + jBlue + kBlue) / 3;
                Color colorAverage = new Color(redAverage, greenAverage, blueAverage);
                //tests to see if any thread average equals user color
                if (colorAverage.equals(stitchColor)) {
                    System.out.println("The color match is: " + i + ", " + j + ", " + k);
                }
            }
        }
    }

    System.out.println("no more matches");
}
Автор: David Pérez Cabrera Размещён: 20.08.2016 09:52

1 плюс

(Я не могу протестировать и отладить код, поскольку он не является SSCCE , поэтому я прошу прощения за любые ошибки).
Рассмотрите следующее, см. Комментарии для деталей:

Color[ ] dmc = {element1, element2, ...};

for( int i =0; i < dmc.length ; i++ )
 {

    //iterate only on values >= i 
   for( int j = i; j< dmc.length; j++ )
   {
       //loop only on values >= j
       for( int k = j; k < dmc.length ; k++ )
       {

           //not needed, you already have i, j, k 
           //int indexI = Arrays.asList(dmc).indexOf(i);
           //int indexJ = Arrays.asList(dmc).indexOf(j);
           //int indexK = Arrays.asList(dmc).indexOf(k);


           //condition is not needed. Allways true. 
           //if( indexI <= indexJ && indexJ <= indexK ) { }

           //bunch of code...

РЕДАКТИРОВАТЬ: короткое демо, чтобы распечатать только требуемые случаи:

        public static void main(String[] args) {

            Color[ ] dmc = {Color.AQUA, Color.AZURE, Color.BEIGE, Color.BLACK, Color.BLUE,
                    Color.BROWN, Color.CHOCOLATE, Color.CYAN,  Color.CRIMSON, Color.FUCHSIA};

            for( int i =0; i < dmc.length ; i++ )
             {

                System.out.printf("\n >>>>>> i= %s", i);
                //iterate only on values >= i
               for( int j = i; j< dmc.length; j++ )
               {
                   System.out.printf("\n         j= %s",j);

                   //loop only on values >= j
                   for( int k = j; k < dmc.length ; k++ )
                   {

                     System.out.printf("\n            k= %s",k);
                   }
               }
             }

        }

РЕДАКТИРОВАТЬ II: После публикации MCVE я изменил его, добавив fastEverage()метод, который должен выполнять те же вычисления, что и everage()с меньшим количеством итераций.
На одном наборе цветов, который я проверил, оба метода имеют одинаковый вывод:

import java.awt.Color;
import java.util.Arrays;

/** Tests to see if user color matches sample colors */
public class Test

{

    //defining sample colors

    Color[] dmc =
        {
                new Color(255,255,255),
                new Color(148,91,128),
                new Color(206,148,186),
                new Color(236,207,225),
                new Color(243,218,228),

        };

    //averages three colors, then tests for match to given color
    public void average()
    {

        //asks for user color
        int stitchRed =  203; 
        int stitchGreen= 164;
        int stitchBlue= 189;

        Color stitchColor= new Color(stitchRed,stitchGreen,stitchBlue);

        //gets averages for dmc colors
        for (Color i:dmc)
        {
            for (Color j:dmc)
            {
                for (Color k:dmc)
                {
                    int indexI = Arrays.asList(dmc).indexOf(i);
                    int indexJ = Arrays.asList(dmc).indexOf(j);
                    int indexK = Arrays.asList(dmc).indexOf(k);
                    if((indexI <= indexJ) && (indexJ <= indexK))
                    {
                        int iRed = i.getRed();
                        int jRed = j.getRed();
                        int kRed = k.getRed();
                        int iGreen = i.getGreen();
                        int kGreen = k.getGreen();
                        int jGreen = j.getGreen();
                        int iBlue = i.getBlue();
                        int jBlue = j.getBlue();
                        int kBlue = k.getBlue();
                        int redAverage = (iRed+jRed+kRed)/3;
                        int greenAverage = (iGreen+ jGreen + kGreen)/3;
                        int blueAverage = (iBlue+jBlue+kBlue)/3;

                        Color colorAverage = new Color(redAverage,greenAverage,blueAverage);

                        //tests to see if any thread average equals user color
                        if (colorAverage.equals(stitchColor))
                        {
                            System.out.println("The color match is: " + i + ", " + j + ", " + k);
                        }

                    }
                }
            }
        }

        System.out.println("no more matches");
    }

    //averages three colors, then tests for match to given color
    public void fastEverage()
    {

        //asks for user color
        int stitchRed =  203; 
        int stitchGreen= 164;
        int stitchBlue= 189;

        Color stitchColor= new Color(stitchRed,stitchGreen,stitchBlue);

        Color colorI, colorJ, colorK;

        for( int i =0; i < dmc.length ; i++ )
        {

            colorI = dmc[i];

            //iterate only on values >= i
            for( int j = i; j< dmc.length; j++ )
            {

                colorJ = dmc[j];

                //loop only on values >= j
                for( int k = j; k < dmc.length ; k++ )
                {

                    colorK = dmc[k];

                    int iRed = colorI.getRed();
                    int jRed = colorJ.getRed();
                    int kRed = colorK.getRed();
                    int iGreen = colorI.getGreen();
                    int kGreen = colorK.getGreen();
                    int jGreen = colorJ.getGreen();
                    int iBlue = colorI.getBlue();
                    int jBlue = colorJ.getBlue();
                    int kBlue = colorK.getBlue();
                    int redAverage = (iRed+jRed+kRed)/3;
                    int greenAverage = (iGreen+ jGreen + kGreen)/3;
                    int blueAverage = (iBlue+jBlue+kBlue)/3;

                    Color colorAverage = new Color(redAverage,greenAverage,blueAverage);

                    //tests to see if any thread average equals user color
                    if (colorAverage.equals(stitchColor))
                    {
                        System.out.println("The color match is: " + colorI + ", " + colorJ + ", " + colorK);
                    }

                }
            }
        }

        System.out.println("no more matches");
    }

    public static void main(String[] args)
    {
        new Test().average();
        new Test().fastEverage();

    }
} 

Разница во времени выполнения между двумя методами в этом тестовом примере является значительной:

время выполнения everage () в нано секундах 84752814

время выполнения fastEverage () в нано секундах 76497255

Автор: c0der Размещён: 20.08.2016 08:57

0 плюса

Учитывая, что вы отфильтровываете много комбинаций в операторе if, одной из оптимизаций было бы использование циклов for только для итерации по элементам, которые вы на самом деле хотите обработать, вместо циклов for-each-loop. Например, i <= j означает, что вы можете начать итерацию в j с i.

Дальнейшие улучшения вполне могут быть возможными в зависимости от ваших намерений, как указано в комментариях.

Автор: Olav Trauschke Размещён: 20.08.2016 08:48

0 плюса

Вы можете попробовать что-то вроде

for( x=1;x<401;x++ )
{
    Color j= dmc[x];
    Color k= dmc[x+1];
    int indexI = Arrays.asList(dmc).indexOf(i);
    int indexJ = Arrays.asList(dmc).indexOf(j);
    int indexK = Arrays.asList(dmc).indexOf(k);
    if( indexI <= indexJ && indexJ <= indexK )
    {
        //bunch of code...

или вы можете попробовать метод equals, определенный в java.lang.Object. Надеюсь, это поможет.

Автор: Tahseen Adit Размещён: 20.08.2016 08:55

0 плюса

Просто идея. Вы можете разделить поиск. В первом поиске есть возможность найти совпадение. Если это так, то вам не нужно искать остальные совпадения. Если нет, вы можете перейти к поиску совпадения.

int i,j,k;    
for(i=0;i<100;i++){
   for(j=i;j<100;j++){
      for(k=j;k<100;k++){
         //..do stuffs.....//
       } 
   }
}
if(matchnotfound){
  for(i=100;i<200;i++){
   for(j=i;j<200;j++){
      for(k=j;k<200;k++){
         //..do stuffs.....//
      } 
    }
  }
}
 if(matchnotfound){
  for(i=200;i<300;i++){
   for(j=i;j<300;j++){
      for(k=j;k<300;k++){
         //..do stuffs.....//
      } 
    }
  }
}
 if(matchnotfound){
  for(i=300;i<400;i++){
   for(j=i;j<400;j++){
      for(k=j;k<400;k++){
         //..do stuffs.....//
      } 
    }
  }
} 
Автор: Tahseen Adit Размещён: 20.08.2016 10:56
Вопросы из категории :
32x32